001 002/*license*\ 003 Codelet: Copyright (C) 2014, Jeff Epstein (aliteralmind __DASH__ github __AT__ yahoo __DOT__ com) 004 005 This software is dual-licensed under the: 006 - Lesser General Public License (LGPL) version 3.0 or, at your option, any later version; 007 - Apache Software License (ASL) version 2.0. 008 009 Either license may be applied at your discretion. More information may be found at 010 - http://en.wikipedia.org/wiki/Multi-licensing. 011 012 The text of both licenses is available in the root directory of this project, under the names "LICENSE_lgpl-3.0.txt" and "LICENSE_asl-2.0.txt". The latest copies may be downloaded at: 013 - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt 014 - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt 015\*license*/ 016package com.github.aliteralmind.codelet; 017 import com.github.xbn.number.LengthInRange; 018 import com.github.xbn.analyze.alter.AlterationNotMadeException; 019 import com.github.aliteralmind.codelet.alter.DefaultAlterGetterUtil; 020 import com.github.xbn.linefilter.alter.AllTextLineAlterer; 021 import com.github.xbn.linefilter.alter.ExpirableTextLineAlterList; 022 import com.github.xbn.linefilter.NewFilteredLineIteratorFor; 023 import com.github.xbn.linefilter.alter.TextLineAlterer; 024 import com.github.xbn.linefilter.FilteredLineIterator; 025 import com.github.xbn.analyze.alter.ExpirableElements; 026 import com.github.xbn.analyze.alter.MultiAlterType; 027 import com.github.xbn.array.CrashIfArray; 028 import com.github.xbn.array.NullContainer; 029 import com.github.xbn.array.NullElement; 030 import com.github.xbn.lang.CrashIfObject; 031 import com.github.xbn.lang.IllegalArgumentStateException; 032 import com.github.xbn.text.CrashIfString; 033 import com.github.xbn.util.lock.AbstractOneWayLockable; 034 import com.google.common.base.Joiner; 035 import java.util.Iterator; 036 import java.util.Objects; 037 import static com.github.aliteralmind.codelet.CodeletBaseConfig.*; 038 import static com.github.xbn.lang.XbnConstants.*; 039/** 040 <P>The instructions returned by a Codelet Customizer, which is used by the taglet-processor to modify its output.</P> 041 042 <A NAME="3_parts"></A><H4><A HREF="#overview"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></A> Codelet: Customizer: <U>Three parts</U></H4> 043 044 <P>A {@code CustomizationInstructions} is the object returned by all <A HREF="#overview">Codelet Customizer</A>s. A {@code CustomizationInstructions} is composed of three items: A <A HREF="#3_parts_filter">line filter</A>, <A HREF="#3_parts_alterer">alterer</A>, and <A HREF="#3_parts_template">template</A>.</P> 045 046 <A NAME="3_parts_filter"></A><H4><A HREF="#3_parts"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></A> Codelet: Customizer: Three parts: <U>Part 1: Line filter</U></H4> 047 048 <P>A line filter is used to keeping only wanted lines, such as a <A HREF="{@docRoot}/overview-summary.html#xmpl_snippet">code snippet</A>, or eliminating lines you define as unwanted. An example is to <A HREF="{@docRoot}/overview-summary.html#xmpl_hello">eliminate</A> the package declaration line and all JavaDoc multi-line comments.</P> 049 050 <P><UL> 051 <LI>Raw object: {@code com.github.xbn.linefilter.}{@link com.github.xbn.linefilter.FilteredLineIterator} (set with {@link #filter(FilteredLineIterator) filter})</LI> 052 <LI>Convenience creators: {@link NewLineFilterFor}, {@link com.github.xbn.linefilter.NewFilteredLineIteratorFor}</LI> 053 </UL></P> 054 055 <A NAME="3_parts_alterer"></A><H4><A HREF="#3_parts"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></A> Codelet: Customizer: Three parts: <U>2: Alterer</U></H4> 056 057 <P>The all-line alterer modifies each line returned (kept) by the filter. A {@linkplain com.github.xbn.analyze.validate.ValidResultFilter filter} may be applied so it does not start until needed, and {@linkplain com.github.xbn.lang.Expirable expires} when complete.</P> 058 059 <P><UL> 060 <LI>Raw objects: {@code com.github.xbn.linefilter.}{@link com.github.xbn.linefilter.AllTextLineAlterer} (set with {@link #alterer(AllTextLineAlterer) alterer}), which is an array of {@link com.github.xbn.linefilter.alter.TextLineAlterer}s (set with {@link #orderedAlterers(Appendable, NullElement, ExpirableElements, MultiAlterType, TextLineAlterer...) orderedAlterers})</LI> 061 <LI>Convenience creators: {@link com.github.aliteralmind.codelet.alter.NewLineAltererFor}, {@link com.github.aliteralmind.codelet.alter.NewJDLinkForWordOccuranceNum}, {@link com.github.xbn.linefilter.alter.NewTextLineAltererFor}</LI> 062 </UL></P> 063 064 <A NAME="3_parts_template"></A><H4><A HREF="#3_parts"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></A> Codelet: Customizer: Three parts: <U>3: Template</U></H4> 065 066 <P>The context into which final output text is placed, and whose {@linkplain CodeletTemplateBase#getRendered(CodeletInstance) rendered} output is what actually replaces the taglet. Templates may be overridden for an individual taglet (by setting one into <!-- GENERIC PARAMETERS FAIL IN @link --><A HREF="#template(T)"><CODE>template</CODE></A>, in a <A HREF="#overview">custom customizer</A>), or for all taglets in a JavaDoc file or an entire package (with {@link TemplateOverrides}).</P> 067 068 <P><UL> 069 <LI>Raw objects: {@linkplain CodeletTemplateBase template} (set with <!-- GENERIC PARAMETERS FAIL IN @link --><A HREF="#template(T)"><CODE>template</CODE></A>) which, at its heart, is a {@code com.github.aliteralmind.templatefeather.FeatherTemplate}</LI> 070 </UL></P> 071 072 <A NAME="overview"></A><H2><A HREF="{@docRoot}/overview-summary.html#overview_description"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></A> Codelet: Customizer: <U>Overview</U></H2> 073 074 <P>A "Codelet Customizer" is a function that returns the <I><A HREF="#skip-navbar_top">instructions</A></I> for tailoring an example code's output. As stated in the <A HREF="{@docRoot}/overview-summary.html#overview_description">overview</A>, common customizations include<UL> 075 <LI>Displaying only a portion of an example's source code: A <A HREF="{@docRoot}/overview-summary.html#xmpl_snippet">code snippet</A>.</LI> 076 <LI><A HREF="{@docRoot}/overview-summary.html#xmpl_hello">Eliminating</A> unwanted lines, such as the package declaration line and all multi-line comments.</LI> 077 <LI>Making the first appearance of a class, function, or object names into a <A HREF="{@docRoot}/overview-summary.html#xmpl_links">clickable JavaDoc link</A>.</LI> 078 </UL></P> 079 080 <P><B>Contents:</B><UL> 081 <LI><B>Taglet syntax:</B> A customizer function is "called" by one or more codelet-taglets. <B>Examples:</B><UL> 082 <LI><A HREF="#xmpl_defaults">Default function name and class location</A></LI> 083 <LI>Defaults with a <A HREF="#proc_custom_post">custom postfix</A></LI> 084 <LI><A HREF="#xmpl_sig">Specifying the class</A> in which the processor function exists</LI> 085 <LI>Specifying <A HREF="#xmpl_params">extra parameters</A> for the customizer function</LI> 086 </UL></P></LI> 087 <LI><B>The customizer function:</B><UL> 088 <LI>Examples: A customizer function that<UL> 089 <LI><A HREF="#func_does_nothing">Does nothing</A>.</LI> 090 <LI>Changes a function, constructor, class, or field name to a <A HREF="{@docRoot}/overview-summary.html#xmpl_links">clickable JavaDoc link</A>.</LI> 091 </UL></LI> 092 <LI><A HREF="#specifications">Specifications</A> and </LI> 093 <LI>Pre-made customizers: {@link BasicCustomizers}</LI> 094 <LI>{@link CustomizationInstructions}: The object returned by the customizer function. Made up of three parts: A <A HREF="#3_parts_filter">line filter</A>, <A HREF="#3_parts_alterer">alterer</A>, and <A HREF="#3_parts_template">template</A></LI> 095 </UL></LI> 096 </UL></P> 097 098 <A NAME="func_does_nothing"></A><H2><A HREF="#overview"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></A> Codelet: Customizer function: <U>Example: A customizer that does nothing</U></H2> 099 100 <P>A customizer function that makes (almost) no changes:</P> 101 102{@.codelet com.github.aliteralmind.codelet.examples.DoNothingCustomizerCompact%lineRangeWithReplace(1, true, "(<SourceCodeTemplate> aCustomizerThatDoesNothing)", "$1", "FIRST", 1, true, "} +//End snippet$", "}", "FIRST", "^ ")} 103 104 <P>Here is the same function, with documentation on the available {@linkplain CodeletBaseConfig#GLOBAL_DEBUG_LEVEL debugging} parameters:</P> 105 106{@.codelet com.github.aliteralmind.codelet.examples.DoNothingCustomizer%lineRangeWithReplace(1, true, "(<SourceCodeTemplate> aCustomizerThatDoesNothing)", "$1", "FIRST", 1, true, "} +//End snippet$", "}", "FIRST", "^ ")} 107 108 <P>This do-nothing customizer uses all {@linkplain #defaults(Appendable, LengthInRange, Appendable, Appendable) defaults}. It<OL> 109 <LI>{@link #unfiltered(Appendable, LengthInRange) Filters no lines},</LI> 110 <LI>{@linkplain com.github.aliteralmind.codelet.alter.DefaultAlterGetter Default alterers} as {@linkplain CodeletBaseConfig#DEFAULT_ALTERERS_CLASS_NAME configured}.</LI> 111 <LI>Uses the {@link #defaultOrOverrideTemplate(Appendable) Default template},</LI> 112 </OL></P> 113 114 <A NAME="specifications"></A><H2><A HREF="#overview"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></A> Codelet: Customizer: <U>Requirements</U></H2> 115 116 <P>The customizer function has the following requirements:<UL> 117 <LI>Its location (containing class) must be <A HREF="#xmpl_sig">explicitely specified</A> in the taglet, or must exist in one of the following <B><U>default classes</U></B>, which are searched in order:<OL> 118 <LI>{@link BasicCustomizers},</LI> 119 <LI>The {@linkplain CodeletInstance#getEnclosingClass() enclosing class}, if it is a class,</LI> 120 <LI>And a class named "{@link TagletOfTypeProcessor#DEFAULT_CUSTOMIZER_CLASS_NAME zCodeletCustomizers}", if one exists in the {@linkplain CodeletInstance#getEnclosingPackage() enclosing package}.</LI> 121 </OL></LI> 122 <LI>It must be {@code static} and</LI> 123 <LI>accessible (it is obtained with <CODE>{@link java.lang.Class Class}.{@link java.lang.Class#getDeclaredMethod(String, Class...) getDeclaredMethod}</CODE> and made accessible with <CODE>theLineProcMethod.{@link java.lang.reflect.AccessibleObject#setAccessible(boolean) setAccessible}(true)</CODE>).</LI> 124 <LI>Its first parameter must be a {@link CodeletInstance CodeletInstance} and second must be a {@link CodeletType}. Both of these parameters are ommitted from all taglets.</LI> 125 <LI>It may contain zero-or-more <A HREF="#xmpl_params">extra parameters</A>, whose types are either primitives or non-{@code null} strings ({@code null} is not possible), as specified by 126 <BR> <CODE>com.github.xbn.util.{@link com.github.xbn.util.SimpleStringSignature SimpleStringSignature}.{@link com.github.xbn.util.SimpleStringSignature#getObjectFromString(String) getObjectFromString} </CODE> 127 <BR>If there are any extra types in the customizer function signature, they must be provided in the {@linkplain TagletOfTypeProcessor#getCustomizerPortion() customizer portion} of every taglet using it. <I>The types, amount, and order of extra parameters, in both the taglet and the customizer function signature, must exactly match.</I></LI> 128 </UL></P> 129 130 <P>When taglets are used in a class (as opposed to <A HREF="http://stackoverflow.com/questions/3644726/javadoc-package-html-or-package-info-java">{@code package-info.java}</A> or your project's <A HREF="http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html#sourcefiles">overview summary</A>), it is encouraged that its customizer functions be in the class, and that these functions are {@code private}. (In the Codelet and <A HREF="http://codelet.aliteralmind.com">XBN-Java</A> projects, this is not possible, as doing so would create <A HREF="http://en.wikipedia.org/wiki/Circular_dependency">circular-dependency</A> nightmare--this is the primary reason for the {@code zCodeletCustomizers} default class.)</P> 131 132 <A NAME="xmpl_defaults"></A><H2><A HREF="#overview"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></A> Codelet: Customizer: Taglet syntax: Example: <U>Default function name and class location</U></H2> 133 134<BLOCKQUOTE>{@code {@.codelet fully.qualified.examples.ExampleClassName%()}}</BLOCKQUOTE> 135 136 <P>This {@code ":()"} shortcut indicates a customizer function with the standard name and class location should be used. In particular:<UL> 137 <LI>Its name is {@code "getSourceCode_ExampleClassName"},</LI> 138 <LI>It has no extra parameters, and it</LI> 139 <LI>Must exist in one of the <A HREF="#specifications">default classes</A></LI> 140 </UL></P> 141 142 <P>{@code {@.codelet fully.qualified.examples.ExampleClassName%()}}</P> 143 144 <P>is equivalent to both</P> 145 146<BLOCKQUOTE>{@code {@.codelet fully.qualified.examples.ExampleClassName:getSourceCode_ExampleClassName()}}</BLOCKQUOTE> 147 148 <P>and</P> 149 150<BLOCKQUOTE>{@code {@.codelet fully.qualified.examples.ExampleClassName:package.of.EnclosingClass#getSourceCode_ExampleClassName()}}</BLOCKQUOTE> 151 152 <P>with one exception: When the processor's function name is explicitely specified, <I>but its class is not</I> (which is true in the first two of the three above), the customizer must exist in one of the default classes</P> 153 154 <A NAME="proc_custom_post"></A><H2><A HREF="#overview"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></A> Codelet: Customizer: Taglet syntax: Example: <U>Defaults with custom postfix</U></H2> 155 156<BLOCKQUOTE>{@code {@.codelet fully.qualified.examples.ExampleClassName%_ExtraStuff()}}</BLOCKQUOTE> 157 158 <P>Same as the <A HREF="#xmpl_defaults">default example</A>, except the underscore-first-character indicates that this is not the customizer's entire function name, rather its <I>postfix</I>.</P> 159 160 <P>This is useful when there are multiple codelets of the same {@linkplain CodeletType type}, for the same example class (or text file).</P> 161 162 <A NAME="xmpl_sig"></A><H2><A HREF="#overview"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></A> Codelet: Customizer: Taglet syntax: Example: <U>Specifying the class in which the processor function exists</U></H2> 163 164 <P>The customizer function can be in any class, which may explicitely specified:</P> 165 166<BLOCKQUOTE>{@code {@.codelet fully.qualified.examples.ExampleClassName:fully.qualified.package.MyCodeletCustomizers#getSource_ExampleClass(true, "See line 12")}}</BLOCKQUOTE> 167 168 <P>If using the <A HREF="#xmpl_defaults">default function name</A>, it may be omitted, although the hash ({@code '#'}) is required:</P> 169 170<BLOCKQUOTE>{@code {@.codelet fully.qualified.examples.ExampleClassName:fully.qualified.package.MyCodeletCustomizers#(true, "See line 12")}}</BLOCKQUOTE> 171 172 <P>Signature formatting is as specified by 173 <BR> <CODE>{@link com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature SimpleMethodSignature}.{@link com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature#newFromStringAndDefaults(Class, Object, String, Class[], Appendable) newFromStringAndDefaults}</CODE> 174 <BR>(Before being provided to {@code newFromStringAndDefaults}, the omitted function name is given its default value [{@code "getSource_ExampleClass"}], as described in this and the <A HREF="#proc_custom_post">previous example</A>. In all cases, {@code SimpleMethodSignature} requires a function name.)</P> 175 176 <A NAME="xmpl_params"></A><H2><A HREF="#overview"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></A> Codelet: Customizer: Taglet syntax: Example: <U>Specifying extra processor parameters</U></H2> 177 178 <P>The default customizer has a single {@link CodeletInstance CodeletInstance} parameter. This is specified in the taglet with either empty parentheses, or no parens at all, as demonstrated in the <A HREF="#xmpl_defaults">default example</A>.</P> 179 180 <P>Extra parameters may be optionally specified, and must be provided <I>both</I> in the function and in any taglet that uses (calls) it. For example, this taglet</P> 181 182<BLOCKQUOTE>{@code {@.codelet fully.qualified.examples.ExampleClassName:(true, "See line 12")}}</BLOCKQUOTE> 183 184 <P>refers to this function:</P> 185 186<BLOCKQUOTE>{@code getSourceCode_ExampleClassName(CodeletInstance taglet, CodeletType needed_defaultAlterType, boolean do_displayLineNums, String annotation)}</BLOCKQUOTE> 187 188 <P>which, since there is no fully-qualified class specified after the {@linkplain CodeletInstance#CUSTOMIZER_PREFIX_CHAR percent sign}, must be in one of the <A HREF="#specifications">default class-locations</A>.</P> 189 190 <P>Parameter formatting is specified by 191 <BR> <CODE>{@link com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature}.{@link com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature#newFromStringAndDefaults(Class, Object, String, Class[], Appendable) newFromStringAndDefaults}</CODE></P> 192 193 <P>This is a "simple" signature. Only {@linkplain com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature#getObjectFromString(String) primitives and strings} are allowed. {@code null} is not possible.</P> 194 195 <P>Extra parameters can also be specified with the <A HREF="#xmpl_defaults">{@code ":()"} shortcut</A>: 196 <BR> {@code {@.codelet fully.qualified.examples.ExampleClassName:((byte)3, false)}}</P> 197 198 199 @since 0.1.0 200 @author Copyright (C) 2014, Jeff Epstein ({@code aliteralmind __DASH__ github __AT__ yahoo __DOT__ com}), dual-licensed under the LGPL (version 3.0 or later) or the ASL (version 2.0). See source code for details. <A HREF="http://codelet.aliteralmind.com">{@code http://codelet.aliteralmind.com}</A>, <A HREF="https://github.com/aliteralmind/codelet">{@code https://github.com/aliteralmind/codelet}</A> 201 **/ 202public class CustomizationInstructions<T extends CodeletTemplateBase> extends AbstractOneWayLockable { 203 private FilteredLineIterator filter ; 204 private String classNameOrFilePathRestricter; 205 private AllTextLineAlterer alterer ; 206 private T template ; 207 private boolean wasTmplSet ; 208 private Appendable dfltTmplDbg ; 209 private final CodeletInstance instance ; 210 private final CodeletType defaultAltersType; 211 /* 212 <P>Create a new instance for any taglet type except {@code {@.codelet.and.out}}.</P> 213 214 <P>Equal to 215 <BR> <CODE>{@link #CustomizationInstructions(CodeletInstance, CodeletType) this}(instance, instance.getType())</CODE></P> 216 217 @param instance May not be {@code null}. 218 public CustomizationInstructions(CodeletInstance instance) { 219 this(instance, CustomizationInstructions.getType(instance)); 220 } 221 private static final CodeletType getType(CodeletInstance instance) { 222 try { 223 return instance.getType(); 224 } catch(RuntimeException rx) { 225 throw CrashIfObject.nullOrReturnCause(instance, "instance", null, rx); 226 } 227 } 228 */ 229 /** 230 <P>Create a new instance.</P> 231 232 <P>Equal to 233 <BR> <CODE>{@link #CustomizationInstructions(CodeletInstance, CodeletType) this}(instance, instance.getType())</CODE></P> 234 235 @param instance May not be {@code null}. 236 @param needed_defaultAlterType The type of {@linkplain com.github.aliteralmind.codelet.alter.DefaultAlterGetter default alterers} needed when using the {@linkplain #defaultOrOverrideTemplate(Appendable) default template}. May not be {@code null}. If <CODE>instance.{@link CodeletInstance#getType() getType}.{@link CodeletType#SOURCE_AND_OUT SOURCE_AND_OUT}</CODE>, then this must be either {@link CodeletType#SOURCE_CODE SOURCE_CODE} or {@link CodeletType#CONSOLE_OUT CONSOLE_OUT}. If <CODE>instance.{@link CodeletInstance#getType() getType}</CODE> is any other type, then {@code needed_defaultAlterType} must be equal to it. 237 **/ 238 public CustomizationInstructions(CodeletInstance instance, CodeletType needed_defaultAlterType) { 239 try { 240 if(!instance.getType().isSourceAndOut()) { 241 if(instance.getType() != needed_defaultAlterType) { 242 throw new IllegalArgumentStateException("instance.getType()." + instance.getType() + ", needed_defaultAlterType=" + needed_defaultAlterType + "."); 243 } 244 } else if(!needed_defaultAlterType.isSourceCode() && 245 !needed_defaultAlterType.isConsoleOut()) { 246 throw new IllegalArgumentStateException("instance.getType().SOURCE_AND_OUT, needed_defaultAlterType must be SOURCE_CODE or CONSOLE_OUT, but is actually " + needed_defaultAlterType + "."); 247 } 248 } catch(RuntimeException rx) { 249 CrashIfObject.nnull(instance, "instance", null); 250 throw CrashIfObject.nullOrReturnCause(needed_defaultAlterType, "needed_defaultAlterType", null, rx); 251 } 252 alterer = null; 253 filter = null; 254 template = null; 255 wasTmplSet = false; 256 classNameOrFilePathRestricter("*"); 257 this.instance = instance; 258 defaultAltersType = needed_defaultAlterType; 259 } 260 /** 261 <P>Make no customizations.</P> 262 263 <P>This calls<OL> 264 <LI>{@link #unfiltered(Appendable, LengthInRange) unfiltered}{@code (dbgDest_ifNonNull)}</LI> 265 <LI><CODE>{@link #orderedAlterers(Appendable, NullElement, ExpirableElements, MultiAlterType, TextLineAlterer...) orderedAlterers}(dbgAllAltr_ifNonNull, {@link com.github.xbn.array.NullElement NullElement}.{@link com.github.xbn.array.NullElement#BAD BAD} 266 <BR> {@link com.github.xbn.analyze.alter.ExpirableElements}.{@link com.github.xbn.analyze.alter.ExpirableElements#OPTIONAL OPTIONAL}, {@link com.github.xbn.analyze.alter.MultiAlterType}.{@link com.github.xbn.analyze.alter.MultiAlterType#CUMULATIVE CUMULATIVE} 267 <BR> {@link com.github.aliteralmind.codelet.alter.DefaultAlterGetterUtil}.{@link com.github.aliteralmind.codelet.alter.DefaultAlterGetterUtil#getDefaultAlterArray(CodeletType) getDefaultAlterArray}({@link #getNeededAlterArrayType() getNeededAlterArrayType}()))</CODE></LI> 268 <LI>{@link #defaultOrOverrideTemplate(Appendable) defaultOrOverrideTemplate}{@code (dbgTemplate_ifNonNull)}</LI> 269 </OL></P> 270 271 @return <I>{@code this}</I> 272 **/ 273 public CustomizationInstructions<T> defaults(Appendable dbgEveryLine_ifNonNull, LengthInRange rangeForEveryLineDebug_ifNonNull, Appendable dbgAllAltr_ifNonNull, Appendable dbgTemplate_ifNonNull) { 274 unfiltered(dbgEveryLine_ifNonNull, rangeForEveryLineDebug_ifNonNull); 275 orderedAlterers(dbgAllAltr_ifNonNull, NullElement.BAD, 276 ExpirableElements.OPTIONAL, MultiAlterType.CUMULATIVE, 277 DefaultAlterGetterUtil.getDefaultAlterArray(getNeededAlterArrayType())); 278 return defaultOrOverrideTemplate(dbgTemplate_ifNonNull); 279 } 280 /* 281 <P>The type of template needed, when using the default template. This is intended for {@link CodeletType#SOURCE_AND_OUT {@.codelet.and.out}} taglets only.</P> 282 283 @return A non-{@code null} type representing the kind of template needed. 284 @see #CustomizationInstructions(CodeletInstance, CodeletType) constructor 285 @see #defaults(Appendable, LengthInRange, Appendable, Appendable) defaults 286 */ 287 public CodeletType getNeededAlterArrayType() { 288 return defaultAltersType; 289 } 290 /** 291 <P>Get the line-filter.</P> 292 293 @see #filter(FilteredLineIterator) 294 **/ 295 public FilteredLineIterator getFilter() { 296 return filter; 297 } 298 /** 299 <P>Get the line-alterer.</P> 300 301 @see #alterer(AllTextLineAlterer) 302 **/ 303 public AllTextLineAlterer getAlterer() { 304 return alterer; 305 } 306 /** 307 <P>Get the template.</P> 308 309 @return <UL> 310 <LI>A non-{@code null} template: When <I>this taglet only</I> should use a non-default (and non-{@linkplain TemplateOverrides override}) template.</LI> 311 <LI>{@code null}: If {@link #wasTemplateSet() wasTemplateSet}{@code ()} is<UL> 312 <LI>{@code true}: No template was set.</LI> 313 <LI>{@code false}: The default (or override) template should be used.</LI> 314 </UL></LI> 315 </UL> 316 @see <CODE><!-- GENERIC PARAMETERS FAIL IN @link --><A HREF="#template(T)">template</A>(T)</CODE> 317 @see #defaultOrOverrideTemplate(Appendable) 318 **/ 319 public T getTemplate() { 320 return template; 321 } 322 public CodeletInstance getInstance() { 323 return instance; 324 } 325 /** 326 <P>Use the default (or override) template.</P> 327 328 <P>This sets<OL> 329 <LI>{@link #getTemplate() getTemplate}{@code ()} to {@code null}</LI> 330 <LI>{@link #wasTemplateSet() wasTemplateSet}{@code ()} to {@code true}</LI> 331 </OL></P> 332 333 <P>This leaves the template object as {@code null} to avoid having to know about the {@link CodeletInstance}, which is required to determine if the default or {@linkplain TemplateOverrides override} template should be used. A {@code null} template value triggers the {@linkplain TagletOfTypeProcessor taglet processor} to get the appropriate template.</P> 334 335 @param dbgDest_ifNonNull When non-{@code null}, this is the debugging destination for all gap-fills. Get with {@link #getDefaultTemplateDebug() getDefaultTemplateDebug}{@code ()} 336 @return <I>{@code this}</I> 337 @exception LockException If {@link #build() build}{@code ()} was already called. 338 @exception IllegalStateException If {@code wasTemplateSet()} is {@code true}. 339 @see <CODE><!-- GENERIC PARAMETERS FAIL IN @link --><A HREF="#template(T)">template</A>(T)</CODE> 340 **/ 341 public CustomizationInstructions<T> defaultOrOverrideTemplate(Appendable dbgDest_ifNonNull) { 342 ciLockedOrTmplAlreadySet(); 343 template = null; 344 wasTmplSet = true; 345 dfltTmplDbg = dbgDest_ifNonNull; 346 return this; 347 } 348 /** 349 <P>Override the template for this codelet-taglet only.</P> 350 351 <P>This sets {@link #wasTemplateSet() wasTemplateSet}{@code ()} to {@code true}.</P> 352 353 @param template May not be {@code null}. Get with {@link #getTemplate() getTemplate}{@code ()}. Note that JavaDoc is multi-threaded, and therefore this template-object must be a new object (not shared among multiple taglets). Use the {@linkplain com.github.aliteralmind.templatefeather.FeatherTemplate#FeatherTemplate(FeatherTemplate, Appendable) copy constructor} or {@link com.github.aliteralmind.templatefeather.FeatherTemplate#getObjectCopy() getObjectCopy}{@code ()} to duplicate it. 354 @return <I>{@code this}</I> 355 @exception LockException If {@link #build() build}{@code ()} was already called. 356 @exception IllegalStateException If {@code wasTemplateSet()} is {@code true}. 357 @see #defaultOrOverrideTemplate(Appendable) 358 **/ 359 public CustomizationInstructions<T> template(T template) { 360 Objects.requireNonNull(template, "template"); 361 ciLockedOrTmplAlreadySet(); 362 this.template = template; 363 wasTmplSet = true; 364 return this; 365 } 366 private void ciLockedOrTmplAlreadySet() { 367 ciLocked(); 368 if(wasTmplSet) { 369 throw new IllegalStateException("Already set"); 370 } 371 } 372 /** 373 <P>Display all lines.</P> 374 375 @return <CODE>{@link #filter(FilteredLineIterator) filter}({@link com.github.xbn.linefilter.NewFilteredLineIteratorFor NewFilteredLineIteratorFor}.{@link com.github.xbn.linefilter.NewFilteredLineIteratorFor#keepAllLinesUnchanged(Iterator, Appendable, LengthInRange) keepAllLinesUnchanged}(null, dbgEveryLine_ifNonNull, rangeForEveryLineDebug_ifNonNull))</CODE> 376 **/ 377 public CustomizationInstructions<T> unfiltered(Appendable dbgEveryLine_ifNonNull, LengthInRange rangeForEveryLineDebug_ifNonNull) { 378 return filter(NewFilteredLineIteratorFor.keepAllLinesUnchanged(null, dbgEveryLine_ifNonNull, rangeForEveryLineDebug_ifNonNull)); 379 } 380 /** 381 <P>Keep or eliminate lines that meet some conditions. Kept lines may be {@linkplain #orderedAlterers(Appendable, NullElement, ExpirableElements, MultiAlterType, TextLineAlterer...) altered}.</P> 382 383 <P>Two examples of filtering lines:<UL> 384 <LI>Displaying only a range of lines--a <A HREF="{@docRoot}/overview-summary.html#xmpl_snippet">code snippet</A>.</LI> 385 <LI><A HREF="{@docRoot}/overview-summary.html#xmpl_hello">Eliminating</A> the package declaration line and all JavaDoc multi-line comments</LI> 386 </UL></P> 387 388 @param filter May not be {@code null}. Get with {@link #getFilter() getFilter}{@code ()}. 389 @return <I>{@code this}</I> 390 @see #unfiltered(Appendable, LengthInRange) 391 @exception LockException If {@link #build() build}{@code ()} was already called. 392 **/ 393 public CustomizationInstructions<T> filter(FilteredLineIterator filter) { 394 ciLocked(); 395 if(filter.wasAllIteratorSet()) { 396 throw new IllegalStateException("filter.wasAllIteratorSet()=true"); 397 } 398 this.filter = filter; 399 return this; 400 } 401 /** 402 <P>Set an ordered series of line-alterers.</P> 403 404 @param alterers May not be {@code null} or empty and, if <CODE>null_element.{@link com.github.xbn.array.NullElement#isBad() isBad}()</CODE> is {@code true}, no elements may be {@code null}. Elements <I>should</I> not be duplicate. 405 @return <CODE>{@link #alterer(AllTextLineAlterer) alterer}(new {@link com.github.xbn.linefilter.AllTextLineAlterer#AllTextLineAlterer(TextLineAlterer[], ExpirableElements, MultiAlterType, Appendable) AllTextLineAlterer}(alterers, xprbl_elements, multi_type, dbgDest_ifNonNull))</CODE> 406 **/ 407 public CustomizationInstructions<T> orderedAlterers(Appendable dbgDest_ifNonNull, NullElement null_element, ExpirableElements xprbl_elements, MultiAlterType multi_type, TextLineAlterer... alterers) { 408 try { 409 if(null_element.isOk()) { 410 alterers = ExpirableTextLineAlterList. 411 getTextLineAltererArrayFromOrderedElementsIfNonNull(null_element, "alterers", alterers); 412 } 413 } catch(RuntimeException rx) { 414 throw CrashIfObject.nullOrReturnCause(null_element, "null_element", null, rx); 415 } 416 CrashIfArray.lengthLessThan(alterers, "alterers", NullContainer.BAD, 1, null); 417 return alterer(new AllTextLineAlterer(alterers, xprbl_elements, multi_type, dbgDest_ifNonNull)); 418 } 419 /** 420 <P>Set the line-alterer.</P> 421 422 @param all_lineAlterer May not be {@code null}. Get with {@link #getAlterer() getAlterer}{@code ()}. 423 @return <I>{@code this}</I> 424 @exception LockException If {@link #build() build}{@code ()} was already called. 425 @see #orderedAlterers(Appendable, NullElement, ExpirableElements, MultiAlterType, TextLineAlterer...) 426 **/ 427 public CustomizationInstructions<T> alterer(AllTextLineAlterer all_lineAlterer) { 428 ciLocked(); 429 Objects.requireNonNull(all_lineAlterer, "all_lineAlterer"); 430 alterer = all_lineAlterer; 431 return this; 432 } 433 /** 434 <P>Wildcard search-term to restrict the classes or files that may utilize this customizer.</P> 435 436 @param whitelist_searchTerm Wildcard search term to match the fully-qualified class name of the example code, or path of the text file that is allowed to use this customizer. Class name examples:<UL> 437 <LI>{@code "com.github.mylibrary.examples.AGoodExample"}</LI> 438 <LI>{@code "com.github.mylibrary.examples.A*Example"}</LI> 439 <LI>{@code "*.examples.A*Example"}</LI> 440 </UL>Text file examples:<UL> 441 <LI>{@code "com/github/mylibrary/examples/doc-files/AGoodExample_input.txt"}</LI> 442 <LI>{@code "com/github/mylibrary/examples/doc-files/*_input.txt"}</LI> 443 <LI>{@code "*_input.txt"}</LI> 444 </UL>May not be {@code null} or empty, and <I>should</I> be a valid wildcard term. Get with {@link #getClassNameOrFilePathRestricter() getClassNameOrFilePathRestricter}{@code ()}. 445 @return <I>{@code this}</I> 446 @see org.apache.commons.io.FilenameUtils#wildcardMatch(String, String) FilenameUtils#wildcardMatch 447 @see TagletOfTypeProcessor#crashIfClassOrFileCannotUseCustomizer(CustomizationInstructions) TagletOfTypeProcessor#crashIfClassOrFileCannotUseCustomizer 448 **/ 449 public CustomizationInstructions<T> classNameOrFilePathRestricter(String whitelist_searchTerm) { 450 CrashIfString.nullEmpty(whitelist_searchTerm, "whitelist_searchTerm", null); 451 classNameOrFilePathRestricter = whitelist_searchTerm; 452 return this; 453 } 454 /** 455 <P>Wildcard search-term to restrict the classes that may utilize this customizer.</P> 456 457 @see #classNameOrFilePathRestricter(String) 458 **/ 459 public String getClassNameOrFilePathRestricter() { 460 return classNameOrFilePathRestricter; 461 } 462 /** 463 <P>Was the template set?.</P> 464 465 @see #getTemplate() 466 @see <CODE><!-- GENERIC PARAMETERS FAIL IN @link --><A HREF="#template(T)">template</A>(T)</CODE> 467 @see #defaultOrOverrideTemplate(Appendable) 468 **/ 469 public boolean wasTemplateSet() { 470 return wasTmplSet; 471 } 472 /** 473 <P>When using the default template only, this is its debug destination.</P> 474 475 @see #getTemplate() 476 @see #defaultOrOverrideTemplate(Appendable) 477 **/ 478 public Appendable getDefaultTemplateDebug() { 479 return dfltTmplDbg; 480 } 481 /** 482 <P>Given all configured customization instructions, transform the raw output (source-code, console-output, or file-text) into its fully-processed form, ready for insertion into the template.</P> 483 484 <P>This logs all alterers that do not make an alteration.</P> 485 486 @exception AlterationNotMadeException If at least one alteration is not made, and it is {@linkplain CodeletBaseConfig#ALTERATION_NOT_MADE_CRASH configured} that a crash should occur (in addition to the warning). 487 **/ 488 public String getCustomizedBody(CodeletInstance instance, Iterator<String> raw_lineItr) { 489 getFilter().setAllIterator(raw_lineItr); 490 String body = getAlterer().getAlteredFromLineObjects(1, getFilter(), LINE_SEP); 491 492 if(!getAlterer().isComplete()) { 493 String msg = getAlterer().appendIncompleteInfo((new StringBuilder())).toString(); 494 if(!isDebugOn(instance, "zzTagletProcessor.codeletfound")) { 495 msg = "(" + instance + ") " + msg; 496 } 497 debuglnAndToConsole(instance, "Codelet warning: " + msg); 498 if(doCrashIfAlterationNotMade()) { 499 throw new AlterationNotMadeException("(CodeletBaseConfig.doCrashIfAlterationNotMade() is true): " + msg); 500 } 501 } 502 503 return body; 504 } 505 /** 506 <P>Declare that this object is ready for use and should be locked.</P> 507 508 @return <I>{@code this}</I> 509 @exception IllegalStateException If the {@linkplain #getFilter() filter} or {@linkplain #getAlterer() alterer} are {@code null}, or {@link #wasTemplateSet() wasTemplateSet}{@code ()} is {@code false}. 510 **/ 511 public CustomizationInstructions<T> build() { 512 crashIfNotReady(); 513 super.lock(); 514 return this; 515 } 516 private void crashIfNotReady() { 517 if(alterer != null && filter != null && 518 wasTmplSet) { 519 return; 520 } 521 522 //At least one is unset 523 String sFltr = ((getFilter() != null) ? null : "filter(FilteredLineIterator)"); 524 String sAlter = ((getAlterer() != null) ? null : "alterer(AllTextLineAlterer)"); 525 String sTmpl = (wasTmplSet ? null : "template(T)"); 526 527/* 528String s = Joiner.on(", ").skipNulls().join(null, null); 529System.out.println("Joiner.on(', ').skipNulls().join(null, null, null) == null: " + (s == null)); 530System.out.println("Joiner.on(', ').skipNulls().join(null, null, null).length(): " + s.length()); 531 532 Output: 533 ... == null: false 534 ....length(): 0 535 */ 536 throw new IllegalStateException("Must set: " + 537 Joiner.on(", ").skipNulls().join(sFltr, sAlter, sTmpl)); 538 } 539}