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.alter.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.aliteralmind.codelet.simplesig.SimpleMethodSignature SimpleMethodSignature}.{@link com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature#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.alter.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}