Java tutorial
/*license*\ Codelet: Copyright (C) 2014, Jeff Epstein (aliteralmind __DASH__ github __AT__ yahoo __DOT__ com) This software is dual-licensed under the: - Lesser General Public License (LGPL) version 3.0 or, at your option, any later version; - Apache Software License (ASL) version 2.0. Either license may be applied at your discretion. More information may be found at - http://en.wikipedia.org/wiki/Multi-licensing. 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: - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt \*license*/ package com.github.aliteralmind.codelet; import com.github.aliteralmind.codelet.alter.DefaultAlterGetter; import com.github.aliteralmind.codelet.alter.DefaultDefaultAlterGetter; import com.github.aliteralmind.codelet.util.AllOnlineOfflineDocRoots; import com.github.aliteralmind.codelet.util.FilenameBlackWhiteList; import com.github.aliteralmind.codelet.util.NamedDebuggers; import com.github.aliteralmind.codelet.util.RefreshOffline; import com.github.aliteralmind.templatefeather.GapCharConfig; import com.github.xbn.io.AppendOrOverwrite; import com.github.xbn.io.DebugLevel; import com.github.xbn.io.NewTextAppenterFor; import com.github.xbn.io.PathMustBe; import com.github.xbn.io.TextAppenter; import com.github.xbn.lang.CrashIfObject; import com.github.xbn.lang.Empty; import com.github.xbn.lang.reflect.ReflectRtxUtil; import com.github.xbn.list.MapUtil; import com.github.xbn.util.IfError; import com.github.xbn.util.PropertiesUtil; import java.io.IOException; import java.net.MalformedURLException; import java.nio.file.AccessDeniedException; import java.nio.file.NoSuchFileException; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.github.xbn.lang.XbnConstants.*; /** <p>Immutable, static, and non-template related configuration, used throughout Codelet, as held in a file named {@link CodeletBootstrap#BASE_CONFIG_FILE_NAME codelet.properties}. Loading is executed by {@link com.github.aliteralmind.codelet.CodeletBootstrap}.</p> <p><i>View <a href="{@docRoot}/../${jd_project_codelet_config_dir}/codelet.properties">{@code {@docRoot}/../${jd_project_codelet_config_dir}/codelet.properties}</a>.)</i></p> <A NAME="base_dirs_other"></a><h2><a href="{@docRoot}/overview-summary.html#overview_description"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a> <u>Codelet: Configuration</u></h2> <p><i>See <a href="{@docRoot}/overview-summary.html#install_configure">getting started</a> in the Codelet installation instructions.</i></p> <p>There are four categories of Codelet global configuration:<ul> <li><a href="#base_dirs_other">Base directories</a> and other</li> <li><a href="#tmpl_defaults">Default templates</a></li> <li><a href="#tmpl_gaps">Template gaps</a></li> <li><a href="#debugging">Debugging and diagnostics</a></li> </ul></p> <A NAME="base_dirs_other"></a><h2><a href="CodeletBaseConfig.html"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a> Codelet: Configuration: <u>Base directories and urls, and other</u></h2> <p><TABLE ALIGN="center" BORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EEEEEE"><TR ALIGN="center" VALIGN="middle"> <TD> </TD> <TD><b><u>Name</u></b></TD> <TD><b><u>Description</u></b></TD> </TR><TR> <TD> </TD> <TD>{@link #BASE_DIR_BASE_DIR base_dir_base_dir}</TD> <TD>For use in all other "base_dir" config vars, with "${BASE}"</TD> </TR><TR> <TD>{@linkplain #getExampleSourceBaseDir() func}</TD> <TD>{@link #EXAMPLE_CLASS_SRC_BASE_DIR example_class_src_base_dir}</TD> <TD>Directory containing the top-most package for the source code of all example classes.</TD> </TR><TR> <TD>{@linkplain #getEnclosingBaseDirList() func}</TD> <TD>{@link #ENCLOSING_CLASS_SRC_BASE_DIRS enclosing_class_src_base_dirs}</TD> <TD>Comma-separated list of all directories containing the <i>top-most package-directories</i> of all codelet-containing source files.</TD> </TR><TR> <TD>{@linkplain #getDefaultAlterGetter() func}</TD> <TD>{@link #DEFAULT_ALTERERS_CLASS_NAME default_alterers_class_name}</TD> <TD>Fully-qualified name of the class that defines alterers used by all pre-built customizers, and are accessible in custom customizers.</TD> </TR></TABLE></p> <A NAME="tmpl_javadoc"></a><h2><a href="CodeletBaseConfig.html"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a> Codelet: Configuration: <u>JavaDoc related</u></h2> <p><TABLE ALIGN="center" BORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EEEEEE"><TR ALIGN="center" VALIGN="middle"> <TD><b><u>Func</u></b></TD> <TD><b><u>Name</u></b></TD> <TD><b><u>Description</u></b></TD> </TR><TR> <TD>{@link #getTargetClassMapInitCapacity() func}</TD> <TD>{@link #UNIQUE_JD_CLASS_TARGET_INIT_CAPACITY unique_jd_class_target_init_capacity}</TD> <TD>Initial capacity of the map holding the JavaDoc target-class shortcut translators.</TD> </TR><TR> <TD>{@link #getOnlinePackageListAttemptCount() func}</TD> <TD>{@link #PKGLIST_ONLINE_ATTEMPT_COUNT pkglist_online_attempts_per_url}</TD> <TD>How many attempts should be made to retrieve each online package-list? If zero, offline only.</TD> </TR><TR> <TD>{@link #getOnlinePackageListAttemptSleepMills() func}</TD> <TD>{@link #PKGLIST_ONLINE_ATTEMPT_SLEEP_MILLS pkglist_online_attempt_sleep_mills}</TD> <TD>How many milliseconds between each failed attempt?</TD> </TR><TR> <TD>{@link #doCrashIfOnlinePackageListFailure() func}</TD> <TD>{@link #PKGLIST_ONLINE_FAILS_BEHAVIOR pkglist_if_online_retrieval_fails__warn_crash}</TD> <TD>If all online-retrieval attempts fail, should only a warning be logged, or should Codelet stop execution? (Offline package-lists are always retrieved.)</TD> </TR><TR> <TD>{@link #doAutoUpdateOfflinePackageLists() func}</TD> <TD>{@link #AUTO_UPDATE_OFFLINE_PACKAGE_LISTS pkglist_auto_refresh_offline__yes_no}</TD> <TD>When online package-lists are retrieved, should offline package lists be refreshed (or created)?</TD> </TR><TR> <TD>{@link #getOfflinePackageListPostfix() func}</TD> <TD>{@link #PKGLIST_OFFLINE_NAME_POSTFIX pkglist_offline_name_postfix}</TD> <TD>Filename postfix, plus optional dot-extension that follows each offline name.</TD> </TR></TABLE></p> <A NAME="tmpl_defaults"></a><h2><a href="CodeletBaseConfig.html"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a> Codelet: Configuration: <u>Default template files</u></h2> <p><TABLE ALIGN="center" BORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EEEEEE"><TR ALIGN="center" VALIGN="middle"> <TD><b><u>Func</u></b></TD> <TD><b><u>Name</u></b></TD> <TD><b><u>Description</u></b></TD> </TR><TR> <TD> </TD> <TD>{@link #DEFAULT_TPML_DIR_PREFIX default_template_directory_prefix}</TD> <TD>Prepended to all {@code "default_*_template_path"} values.</TD> </TR><TR> <TD>{@linkplain #getDefaultSourceCodeTemplatePath() func}</TD> <TD>{@link #DEFAULT_SRC_CODE_TMPL_PATH default_source_codelet_template_path}</TD> <TD>Path to the (source-code) {@link CodeletType#SOURCE_CODE {@.codelet}} taglet's default template.</TD> </TR><TR> <TD>{@linkplain #getDefaultConsoleOutTemplatePath() func}</TD> <TD>{@link #DEFAULT_DOT_OUT_TMPL_PATH default_dot_out_template_path}</TD> <TD>Path to the {@link CodeletType#CONSOLE_OUT {@.codelet.out}} taglet's default template.</TD> </TR><TR> <TD>{@linkplain #getDefaultFileTextTemplatePath() func}</TD> <TD>{@link #DEFAULT_FILE_TEXT_TMPL_PATH default_file_textlet_template_path}</TD> <TD>Path to the default template used for the {@link CodeletType#FILE_TEXT {@.file.textlet}} taglet's default template.</TD> </TR><TR> <TD>{@linkplain #getDefaultSourceAndOutTemplatePath() func}</TD> <TD>{@link #DEFAULT_AND_OUT_TMPL_PATH default_and_out_template_path}</TD> <TD>Path to the {@link CodeletType#SOURCE_AND_OUT {@.codelet.and.out}} taglet's default template.</TD> </TR><TR> <TD>{@linkplain #getCustomTemplateDir() func}</TD> <TD>{@link #USER_TEMPLATE_BASE_DIR user_template_base_dir}</TD> <TD>Directory in which any custom (user-created) templates are stored.</TD> </TR></TABLE></p> <A NAME="tmpl_gaps"></a><h2><a href="CodeletBaseConfig.html"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a> Codelet: Configuration: <u>Template gaps</u></h2> <p><TABLE ALIGN="center" BORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EEEEEE"><TR ALIGN="center" VALIGN="middle"> <TD><b><u>Func</u></b></TD> <TD><b><u>Name</u></b></TD> <TD><b><u>Description</u></b></TD> </TR><TR> <TD>{@linkplain #getGapCharConfig() func}</TD> <TD>{@link #GAP_NAME_PREFIX_CHAR gap_name_prefix_char}</TD> <TD>Character placed immediately before all template gap names.</TD> </TR><TR> <TD> </TD> <TD>{@link #GAP_NAME_POSTFIX_CHAR gap_name_postfix_char}</TD> <TD>Character placed immediately after gap names.</TD> </TR><TR> <TD> </TD> <TD>{@link #GAP_NAME_LITERAL_PREFIX gap_literal_prefix_char}</TD> <TD>Literal representation of the prefix char, for use in between texts.</TD> </TR><TR> <TD> </TD> <TD>{@link #GAP_NAME_LITERAL_POSTFIX gap_literal_name_postfix_char}</TD> <TD>Literal representation of the postfix char, for use in between texts.</TD> </TR><TR> <TD>{@linkplain #getUserExtraGapsClassName() func}</TD> <TD>{@link #USER_EXTRA_GAPS_CLASS_NAME user_extra_gaps_class}</TD> <TD>Fully-qualified name of the class that defines extra gaps that can be placed in Codelet templates.</TD> </TR></TABLE></p> <A NAME="debugging"></a><h2><a href="CodeletBaseConfig.html"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a> Codelet: Configuration: <u>Debugging and diagnostics</u></h2> <p><TABLE ALIGN="center" BORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EEEEEE"><TR ALIGN="center" VALIGN="middle"> <TD><b><u>Func</u></b></TD> <TD><b><u>Name</u></b></TD> <TD><b><u>Description</u></b></TD> </TR><TR> <TD>{@linkplain #getGlobalDebugLevel() func}</TD> <TD>{@link #GLOBAL_DEBUG_LEVEL global_debugging__off_12345}</TD> <TD>Amount that information that should be logged in every taglet-call</TD> </TR><TR> <TD>{@linkplain #getDebugAptr() func}</TD> <TD>{@link #DEBUG_DESTINATION debug_to__console_path}</TD> <TD>If debugging is on, where should it be written to?</TD> </TR><TR> <TD>{@linkplain #getBlackWhiteList() func}</TD> <TD>{@link #BLACK_WHITE_LIST_TYPE list_type__black_white_off}</TD> <TD>For ignoring all codelets in a single file, or in an entire package.</TD> </TR><TR> <TD> </TD> <TD>{@link #BLACK_WHITE_LIST_CASE list_case__ignore_require_system}</TD> <TD>When comparing class names against the black/white list, how should case be considered?.</TD> </TR><TR> <TD> </TD> <TD>{@link #BLACK_WHITE_PROPER_LIST comma_delimited_proper_list}</TD> <TD>List of wildcard matches that determine which files or packages should be recognized.</TD> </TR><TR> <TD> </TD> <TD>{@link #BLACK_WHITE_OVERRIDE_LIST comma_delimited_override_list}</TD> <TD>List of wildcard matches that trump those in the "proper"-list.</TD> </TR><TR> <TD>{@link #doCrashIfAlterationNotMade() func}</TD> <TD>{@link #ALTERATION_NOT_MADE_CRASH if_alteration_not_made_crash__yes_no}</TD> <TD>If a customizer attempts to make an alteration, but cannot (such as when the find-what text is not found in the example code), should a crash occur, in addition to the warning?</TD> </TR></TABLE></p> * @since 0.1.0 * @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> **/ public enum CodeletBaseConfig { INSTANCE; private static boolean wasLoaded; private static DebugLevel dbgLevel; private static TextAppenter dbgAptr; private static boolean doDebugToConsole; private static GapCharConfig gapCharConfig; private static String xmplSrcBaseDir; private static String[] enclosingBaseDirs; private static List<String> enclosingBaseDirList; private static DefaultAlterGetter defaultAlterGetter; private static String customTmplDir; private static String defaultSrcTmplPath; private static String defaultOutTmplPath; private static String defaultSrcAndOutTmplPath; private static String defaultFileTextTmplPath; private static String userExtraGapsClassNm; private static FilenameBlackWhiteList blackWhiteList; private static int jdTgtClsMapInitCapacity; private static boolean doCrashIfAlterNotMade; private static AllOnlineOfflineDocRoots allDocRoots; private static int onlinePkgLstAttemptCount; private static long onlinePkgLstAttemptSleepMills; private static boolean doCrashIfOnlinePkgLstFails; private static boolean doAutoUpdateOfflinePkgLsts; private static String offlinePkgLstNamePost; private static NamedDebuggers namedDebugLevels; /** <p>Default value for {@code pkglist_online_attempts_per_url} when not provided--Equal to {@code 5}.</p> * @see #PKGLIST_ONLINE_ATTEMPT_COUNT */ public static final int DEFAULT_ONLINE_PKGLST_ATTEMPTS = 5; /** <p>Default value for {@code pkglist_online_attempts_per_url} when not provided--Equal to {@code 500}.</p> * @see #PKGLIST_ONLINE_ATTEMPT_SLEEP_MILLS */ public static final int DEFAULT_ONLINE_PKGLST_SLEEP_MILLS = 500; /** <p>Default value for {@code unique_jd_class_target_init_capacity} when not provided--Equal to {@code 100}.</p> * @see #UNIQUE_JD_CLASS_TARGET_INIT_CAPACITY */ public static final int DEFAULT_JD_TARGET_CLASS_MAP_INIT_CAPACITY = 100; /* <p>Should the {@linkplain CodeletBootstrap#EXTERNAL_DOC_ROOT_URL_FILE offline versions} of external library {@code package-list} files be automatically updated?--Equal to {@code "pkglist_auto_refresh_offline__yes_no"}.</p> <p>Must be one of two values:<ul> <li>{@code "yes"}: Every execution of {@code javadoc.exe} updates the offline files to equal the most recent content of their online counterparts.</li> <li>{@code "no"}: Offline files must be manually (created and) updated. If they do not exist, an error occurs.</li> </ul></p> * @see #doAutoUpdateOfflinePackageLists() public static final String AUTO_UPDATE_OFFLINE_PACKAGE_LISTS = "pkglist_auto_refresh_offline__yes_no"; */ /** <p>Directory containing the top-most package for the source code of all example classes--Name is {@code "example_class_src_base_dir"}.</p> <p>If this is an example code</p> <p><code>com.github.smith.examples.AGoodExample</code></p> <p>and the path to its source code is</p> <p>{@code C:\java_code\com\github\smith\examples\AGoodExample.java}</p> <p>set this to</p> <p>{@code "C:\java_code\"}</p> <p>The ending file-separator is requried. This may or may not be equal to {@link #ENCLOSING_CLASS_SRC_BASE_DIRS}. And its value may include the "{@link #BASE_DIR_BASE_DIR base_dir_base_dir}".</p> * @see <code><a href="#base_dirs_other"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #getExampleSourceBaseDir() */ public static final String EXAMPLE_CLASS_SRC_BASE_DIR = "example_class_src_base_dir"; /** <p>For ignoring all codelets in a single file, or in an entire package--Name is {@code "list_type__black_white_off"}.</p> <p><i>({@linkplain #GLOBAL_DEBUG_LEVEL Debugging Codelet} in general)</i></p> <p><ul> <li>{@code list_type__black_white_off}: Must be one of the following:<ul> <li>{@code "black"}: The codelets in any files or packages that match an item in the proper-list, are ignored. Those not matched are processed.</li> <li>{@code "white"}: The codelets in any files or packages that match an item in the proper-list, are processed.</li> <li>{@code "off"}: All codelets are processed.</li> </ul></li> <li>{@code list_case__ignore_require_system}: When comparing the fully-qualified name of the enclosing file (or package), should letter case be considered?<ul> <li>{@code "ignore"}: Case {@linkplain org.apache.commons.io.IOCase#INSENSITIVE insensitive}</li> <li>{@code "require"}: Case ({@linkplain org.apache.commons.io.IOCase#SENSITIVE sensitive})</li> <li>{@code "system"}: Case sensitivity is determined by the ({@linkplain org.apache.commons.io.IOCase#SYSTEM operating system})</li> </ul></li> <li>{@code comma_delimited_proper_list}: The list of wildcard matches that, when the fully-qualified file or package is matched, are ignored or processed (as determined by the black/white setting). Whitelist examples:<ul> <li>To process all codelets in all packages: {@code "*"}</li> <li>To process codelets only in a single package: {@code "github.com.myname.text.*"}</li> <li>To process codelets in one package, plus a subset of files in another: {@code "github.com.myname.text.*,github.com.myname.io.Get*ForPath"}</li> </ul>Do not put any dot-postfix after the file names (such as {@code ".java"} or {@code ".html"}). <i>For the overview summary ({@code "overview-summary.html"}), use {@code "OVERVIEW_SUMMARY"}. For package summary's ({@code "package-info.java"} or {@code "package-summary.html"}), use {@code "PACKAGE_SUMMARY"}.</i></li> <li>{@code comma_delimited_override_list}: The list of wildcard matches that trump those in the proper-list. This list is only applicable when a file or package is matched by a proper-item. Examples:<ul> <li>To process all codelets except those in a single file, using a whitelist (the value of {@code list_case__ignore_require_system} is required, but left out of these examples): <blockquote><pre>list_type__black_white_off=white comma_delimited_proper_list=fully.qualified.class.name.of.FileToProcess comma_delimited_override_list=</pre></blockquote></li> <li>Using a blacklist: <blockquote><pre>list_type__black_white_off=black comma_delimited_proper_list=* comma_delimited_override_list=*.FileToProcess</pre></blockquote></li> <li>To process codelets only in a single package: {@code "github.com.myname.text.*"}</li> <li>To process codelets in one package, plus a subset of files in another: {@code "github.com.myname.text.*,github.com.myname.io.Get*ForPath"}</li> </ul>Asterisks ({@code '*'}) indicate one-or-more of any character, and question marks ({@code '?'}) mean exactly one of any character. So, if {@code FileToProcess} is a unique filename across all project directories {@code comma_delimited_proper_list} could alternatively be <br/> {@code *.FileToProcess} <br/>If it is not unique: <br/> {@code *.name.of.FileToProcess}</li> </ul>These values are processed by <code>{@link com.github.aliteralmind.codelet.util.FilenameBlackWhiteList FilenameBlackWhiteList}.{@link com.github.aliteralmind.codelet.util.FilenameBlackWhiteList#newFromConfigStringVars(String, String, String, String, String, Appendable, Appendable) newFromConfigStringVars}</code></p> * @see <code><a href="#debugging"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see com.github.aliteralmind.codelet.util.FilenameBlackWhiteList * @see #getBlackWhiteList() * @see #BLACK_WHITE_LIST_CASE * @see #BLACK_WHITE_PROPER_LIST * @see #BLACK_WHITE_OVERRIDE_LIST */ public static final String BLACK_WHITE_LIST_TYPE = "list_type__black_white_off"; /** <p>If a customizer attempts to make an alteration, but cannot (such as when the find-what text is not found in the example code), should a crash occur, in addition to the warning?--Name is {@code "if_alteration_not_made_crash__yes_no"}.</p> <p>Must be one of two values:<ul> <li>{@code "no"}: Only warnings will be logged.</li> <li>{@code "yes"}: In addition to the warning, and exception is thrown.</li> </ul></p> * @see #doCrashIfAlterationNotMade() * @see com.github.aliteralmind.codelet.alter.NewJDLinkForWordOccuranceNum * @see CustomizationInstructions#getCustomizedBody(CodeletInstance, Iterator) CustomizationInstructions#getCustomizedBody */ public static final String ALTERATION_NOT_MADE_CRASH = "if_alteration_not_made_crash__yes_no"; /** <p>Initial capacity of the map holding the JavaDoc target-class shortcut translators--Name is {@code "unique_jd_class_target_init_capacity"}. When not provided (set to the empty-string: {@code ""}), this defaults to {@link #DEFAULT_JD_TARGET_CLASS_MAP_INIT_CAPACITY}.</p> * @see <code><a href="#tmpl_javadoc"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see com.github.aliteralmind.codelet.alter.NewJDLinkForWordOccuranceNum#getAllParamSigsForLinkTarget(Class) * @see #getTargetClassMapInitCapacity() */ public static final String UNIQUE_JD_CLASS_TARGET_INIT_CAPACITY = "unique_jd_class_target_init_capacity"; /** <p>When comparing class names against the black/white list, how should case be considered?--Name is {@code "list_case__ignore_require_system"}.</p> * @see <code><a href="#debugging"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #BLACK_WHITE_LIST_TYPE */ public static final String BLACK_WHITE_LIST_CASE = "list_case__ignore_require_system"; /** <p>List of wildcard matches that determine which files or packages should be recognized--Name is {@code "comma_delimited_proper_list"}.</p> * @see <code><a href="#debugging"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #BLACK_WHITE_LIST_TYPE */ public static final String BLACK_WHITE_PROPER_LIST = "comma_delimited_proper_list"; /** <p>List of wildcard matches that trump those in the "proper"-list--Name is {@code "comma_delimited_proper_list"}.</p> * @see <code><a href="#debugging"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #BLACK_WHITE_LIST_TYPE */ public static final String BLACK_WHITE_OVERRIDE_LIST = "comma_delimited_override_list"; /** <p>Comma-separated list of all directories containing the <i>top-most package-directory</i> of all codelet-containing source files--Name is {@code "enclosing_class_src_base_dirs"}.</p> <p>If all codelet-containing source code exists in a single directory:</p> <blockquote><pre>C:\java_code\src\project_overview.html C:\java_code\src\com\github\xbn\lang\AClass.java C:\java_code\src\com\github\xbn\examples\lang\Demo_AClass.java C:\java_code\src\com\github\xbn\examples\test\UnitTest_AClass.java</pre></blockquote> <p>Then set this to {@code "C:\java_code\src\"}</p> <p>If there are multiple directories:</p> <blockquote><pre>C:\java_code\supplemental_files\project_overview.html C:\java_code\src\com\github\xbn\lang\AClass.java C:\java_code\src\com\github\xbn\examples\lang\Demo_AClass.java C:\java_code\src\com\github\xbn\examples\test\UnitTest_AClass.java</pre></blockquote> <p>Then use <br/> {@code C:\java_code\src\,C:\java_code\supplemental_files\} <br/>or set "{@link #BASE_DIR_BASE_DIR base_dir_base_dir}" to {@code "C:\java_code\"}, and use <br/> {@code ${BASE}src\,${BASE}supplemental_files\}</p> <p><b>At least one directory-value is required.</b></p> <p>The project-overview file, as <a href="http://docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/javadoc.html#overview">configured</a> into JavaDoc, must exist in one of these directories. If its path is <br/> {@code C:\java_code\src\project_overview.html} <br/>and {@code C:\java_code\src\} is its enclosing class base-directory, then its fully-qualified name as required by the {@linkplain #BLACK_WHITE_LIST_TYPE blacklist} is {@code "project_overview"}. And in the hand {@linkplain TemplateOverrides template-overrides} configuration file, it is {@code "project_overview.html"}.</p> <p>This value may be equal to (or contain) {@link #EXAMPLE_CLASS_SRC_BASE_DIR}.</p> * @see <code><a href="#base_dirs_other"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #BASE_DIR_BASE_DIR * @see #getEnclosingBaseDirList() */ public static final String ENCLOSING_CLASS_SRC_BASE_DIRS = "enclosing_class_src_base_dirs"; /** <p>For use in all other "base_dir" config vars, with "${BASE}"--Name is {@code "base_dir_base_dir"}.</p> <p>Only the first use is recognized. If this variable equals <br/> <code>C:\java_code\</code> <br/>then <br/> <code>${BASE}hello${BASE}</code> <br/>is translated to <br/> <code>C:\java_code\hello${BASE}</code></p> * @see <code><a href="#base_dirs_other"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #ENCLOSING_CLASS_SRC_BASE_DIRS */ public static final String BASE_DIR_BASE_DIR = "base_dir_base_dir"; /** <p>Name of the sub-directory in which the offline versions of external library {@code "package-list"} files are stored--Equal to {@code "offline_package_lists"}.</p> <p>This is located in the Codelet {@linkplain CodeletBootstrap#getCodeletConfigDir() configuration directory}.</p> * @see <code><a href="#tmpl_javadoc"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see CodeletBootstrap#EXTERNAL_DOC_ROOT_URL_FILE * @see #PKGLIST_ONLINE_ATTEMPT_COUNT */ public static final String OFFLINE_PACKAGE_LIST_DIR_NAME = "offline_package_lists"; /** <p>How many attempts should be made to retrieve each online package-list? If zero, offline only--Name is {@code "pkglist_online_attempts_per_url"}.</p> <p>Must be an integer zero or greater. If not provided (equal to the empty-string: {@code ""}), this defaults to {@link #DEFAULT_ONLINE_PKGLST_ATTEMPTS}.</p> * @see <code><a href="#tmpl_javadoc"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #getAllJavaDocRoots() * @see #getOnlinePackageListAttemptCount() */ public static final String PKGLIST_ONLINE_ATTEMPT_COUNT = "pkglist_online_attempts_per_url"; /** <p>How many milliseconds between each failed attempt?--Name is {@code "pkglist_online_attempt_sleep_mills"}.</p> <p>Must be an integer zero or greater. If not provided (equal to the empty-string: {@code ""}), thi defaults to {@link #DEFAULT_ONLINE_PKGLST_SLEEP_MILLS}.</p> * @see <code><a href="#tmpl_javadoc"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #getOnlinePackageListAttemptSleepMills() * @see #PKGLIST_ONLINE_ATTEMPT_COUNT */ public static final String PKGLIST_ONLINE_ATTEMPT_SLEEP_MILLS = "pkglist_online_attempt_sleep_mills"; /** <p>If all online-retrieval attempts fail, should only a warning be logged, or should Codelet stop execution? (Offline package-lists are always retrieved)--Name is {@code "pkglist_if_online_retrieval_fails__warn_crash"}.</p> * @see <code><a href="#tmpl_javadoc"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #doCrashIfOnlinePackageListFailure() * @see #PKGLIST_ONLINE_ATTEMPT_COUNT */ public static final String PKGLIST_ONLINE_FAILS_BEHAVIOR = "pkglist_if_online_retrieval_fails__warn_crash"; /** <p>When online package-lists are retrieved, should offline package lists be refreshed (or created)?--Name is {@code "pkglist_auto_refresh_offline__yes_no"}.</p> * @see <code><a href="#tmpl_javadoc"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #doAutoUpdateOfflinePackageLists() * @see #PKGLIST_ONLINE_ATTEMPT_COUNT */ public static final String AUTO_UPDATE_OFFLINE_PACKAGE_LISTS = "pkglist_auto_refresh_offline__yes_no"; /** <p>Filename postfix, plus optional dot-extension that follows each offline name.--Name is {@code "pkglist_offline_name_postfix"}.</p> * @see <code><a href="#tmpl_javadoc"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #getOfflinePackageListPostfix() * @see #PKGLIST_ONLINE_ATTEMPT_COUNT */ public static final String PKGLIST_OFFLINE_NAME_POSTFIX = "pkglist_offline_name_postfix"; /** <p>Amount that information that should be logged in every taglet-call--Name is {@code "global_debugging__off_12345"}.</p> <p>This {@linkplain com.github.xbn.io.DebugLevel#getFromStringOff12345(String, String) must be equal to}<ul> <li>{@code "off"}: No automated debugging.</li> <li>A digit between one and five: An arbitrary number representing the amount of debugging information that will be output.</li> </ul></p> <p>This is the {@linkplain #getGlobalDebugLevel() global debugging level} used by all taglets. An individual taglet may override this by prepending "{@link CodeletInstance#DEBUG_LEVEL_PREFIX_PREFIX [DEBUG_LEVEL_#]}" to its text. If the {@linkplain CodeletInstance#getDebugLevel() taglet's level} is lower than or equal to the global level, it is ignored.</p> <p>For very specific control over what is output <i>without having to recompile code</i>, create a "{@link CodeletBootstrap#NAMED_DEBUGGERS_CONFIG_FILE named debugger}", each of which has a level-number associated to it. If the actual (global or taglet) debugging level is equal-to-or-higher, it's information is output.</p> <h4>What is logged</h4> <p>The examples in this section assumes that {@code CodeletBaseConfig} is <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/language/static-import.html">statically imported</a> "en masse": <br/> <code>import static com.github.aliteralmind.codelet.CodeletBaseConfig.*;</code></p> <p>To output something even if the debugging level is {@code "off"}:</p> <blockquote><code>{@link #debugln(Object) debugln}("Important information");</code></blockquote> <p> To output only when global debugging is on (level one or greater):</p> <blockquote><code>if({@link #isDebugOn(CodeletInstance) isDebugOn}(null)) { debugln("Important information") }</code></blockquote> <p>To output when global debugging is at least level three:</p> <blockquote><code>if({@link #isDebugOn(int, CodeletInstance) isDebugOn}(3, null)) { debugln("Important information") }</code></blockquote> <p>To output when global or taglet debugging is at least three:</p> <blockquote><code>if({@link #isDebugOn(int, CodeletInstance) isDebugOn}(3, <i>[the-taglet-instance]</i>)) { debugln("Important information") }</code></blockquote> * @see <code><a href="#debugging"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #DEBUG_DESTINATION * @see #getGlobalDebugLevel() * @see <a href="{@docRoot}/overview-summary.html#xmpl_links_debug">Overview JavaDoc link example</a> */ public static final String GLOBAL_DEBUG_LEVEL = "global_debugging__off_12345"; /** <p>When debugging is on, where should output be written to?--Name is {@code "debug_to__console_path"}.</p> <p>This must be one of two values:<ul> <li>{@code "console"}: Sets {@link #getDebugAptr() getDebugAptr}{@code ()} to <code>{@link com.github.xbn.io.TextAppenter TextAppenter}.{@link com.github.xbn.io.TextAppenter#CONSOLE CONSOLE}</code></li> <li>The path to a writable file: Sets {@code getDebugAptr()} to <br/> <code>{@link com.github.xbn.io.NewTextAppenterFor NewTextAppenterFor}.{@link com.github.xbn.io.NewTextAppenterFor#file(String, AppendOrOverwrite) file}(dbgAptrStr, {@link com.github.xbn.io.AppendOrOverwrite AppendOrOverwrite}.{@link com.github.xbn.io.AppendOrOverwrite#APPEND APPEND})</code></li> </ul>In order to allow debugging at any time, even when the {@linkplain #GLOBAL_DEBUG_LEVEL debugging level} is set to off, this value is required.</p> * @see <code><a href="#debugging"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #getDebugAptr() */ public static final String DEBUG_DESTINATION = "debug_to__console_path"; /** <p>Prepended to all {@code "default_*_template_path"} values--Name is {@code "default_template_directory_prefix"}.</p> <p>This value may be the empty string ({@code ""}), and may include the "{@link #BASE_DIR_BASE_DIR base_dir_base_dir}"</p> <p>All default template paths are required, must exist and be readable, and must refer to a valid Codelet template of its type. They may be in any of the following locations, which are searched for in order:<ol> <li>A file name, which exists in the same directory as this configuration file.</li> <li>A relative path, which exists off of the directory from which the Java Virtual Machine was invoked.</li> <li>An absolute path.</li> </ol> <p>View the locally installed templates:<ul> <li><a href="{@docRoot}/../${jd_project_codelet_config_dir}/default_templates/source_code.txt">{@code {@.codelet}}</a></li> <li><a href="{@docRoot}/../${jd_project_codelet_config_dir}/default_templates/console_out.txt">{@code {@.codelet.out}}</a></li> <li><a href="{@docRoot}/../${jd_project_codelet_config_dir}/default_templates/src_and_out.txt">{@code {@.codelet.and.out}}</a></li> <li><a href="{@docRoot}/../${jd_project_codelet_config_dir}/default_templates/file_text.txt">{@code {@.file.textlet}}</a></li> </ul>These links assume the default template directory is "{@code {@docRoot}/../${jd_project_codelet_config_dir}/default_templates/}"</p> * @see <code><a href="#tmpl_defaults"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></code> * @see #DEFAULT_SRC_CODE_TMPL_PATH * @see #DEFAULT_DOT_OUT_TMPL_PATH * @see #DEFAULT_FILE_TEXT_TMPL_PATH * @see #DEFAULT_AND_OUT_TMPL_PATH * @see #USER_EXTRA_GAPS_CLASS_NAME * @see #USER_TEMPLATE_BASE_DIR */ public static final String DEFAULT_TPML_DIR_PREFIX = "default_template_directory_prefix"; /** <p>Path to the default {@linkplain type.SourceCodeTemplate template} used for (source-code) {@link CodeletType#SOURCE_CODE {@.codelet}} taglets--Name is {@code "default_source_codelet_template_path"}.</p> * @see <code><a href="#tmpl_defaults"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></code> * @see #DEFAULT_TPML_DIR_PREFIX * @see #getDefaultSourceCodeTemplatePath() */ public static final String DEFAULT_SRC_CODE_TMPL_PATH = "default_source_codelet_template_path"; /** <p>Path to the default {@linkplain type.ConsoleOutTemplate template} used for {@link CodeletType#CONSOLE_OUT {@.codelet.out}} taglets--Name is {@code "default_dot_out_template_path"}.</p> * @see <code><a href="#tmpl_defaults"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></code> * @see #DEFAULT_TPML_DIR_PREFIX * @see #getDefaultConsoleOutTemplatePath() */ public static final String DEFAULT_DOT_OUT_TMPL_PATH = "default_dot_out_template_path"; /** <p>Path to the default {@linkplain type.SourceAndOutTemplate template} used for {@link CodeletType#SOURCE_AND_OUT {@.codelet.and.out}} taglets--Name is {@code "default_and_out_template_path"}.</p> * @see <code><a href="#tmpl_defaults"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></code> * @see #DEFAULT_TPML_DIR_PREFIX * @see #getDefaultSourceAndOutTemplatePath() */ public static final String DEFAULT_AND_OUT_TMPL_PATH = "default_and_out_template_path"; /** <p>Path to the default {@linkplain type.FileTextTemplate template} used for {@link CodeletType#FILE_TEXT {@.file.textlet}} taglets--Name is {@code "default_file_textlet_template_path"}.</p> * @see <code><a href="#tmpl_defaults"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></code> * @see #DEFAULT_TPML_DIR_PREFIX * @see #getDefaultFileTextTemplatePath() */ public static final String DEFAULT_FILE_TEXT_TMPL_PATH = "default_file_textlet_template_path"; /** <p>Fully-qualified name of the class that defines {@linkplain CustomizationInstructions#orderedAlterers(Appendable, NullElement, ExpirableElements, MultiAlterType, TextLineAlterer...) alterers} used by all {@linkplain BasicCustomizers pre-built customizers}, and are accessible in <a href="CustomizationInstructions.html#overview">custom customizers</a>--Name is {@code "default_alterers_class_name"}.</p> <p>When provided, the class it represents must {@linkplain java.lang.Class#forName(String) exist}, implement {@link com.github.aliteralmind.codelet.alter.DefaultAlterGetter}, and have a no-parameter constructor. When not provided (equal to the empty-string {@code ""}), {@link DefaultDefaultAlterGetter} is used.</p> * @see <code><a href="#base_dirs_other"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></a></code> * @see #getDefaultAlterGetter() */ public static final String DEFAULT_ALTERERS_CLASS_NAME = "default_alterers_class_name"; /** <p>Character placed immediately before gap names--Name is {@code "gap_name_prefix_char"}.</p> <p>If not provided, defaults to <code>{@link com.github.aliteralmind.templatefeather.GapCharConfig GapCharConfig}.{@link com.github.aliteralmind.templatefeather.GapCharConfig#DEFAULT_PREFIX_CHAR DEFAULT_PREFIX_CHAR}</code></p> * @see <code><a href="#tmpl_gaps"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></code> * @see #GAP_NAME_POSTFIX_CHAR * @see #GAP_NAME_LITERAL_PREFIX * @see #getGapCharConfig() */ public static final String GAP_NAME_PREFIX_CHAR = "gap_name_prefix_char"; /** <p>Character placed immediately after gap names--Name is {@code "gap_name_postfix_char"}.</p> <p>If not provided, defaults to <code>{@link com.github.aliteralmind.templatefeather.GapCharConfig GapCharConfig}.{@link com.github.aliteralmind.templatefeather.GapCharConfig#DEFAULT_POSTFIX_CHAR DEFAULT_PREFIX_CHAR}</code></p> * @see <code><a href="#tmpl_gaps"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></code> * @see #GAP_NAME_PREFIX_CHAR * @see #GAP_NAME_LITERAL_POSTFIX * @see #getGapCharConfig() */ public static final String GAP_NAME_POSTFIX_CHAR = "gap_name_postfix_char"; /** <p>Literal representation of the prefix char, for use in between texts--Name is {@code "gap_literal_prefix_char"}.</p> <p>If not provided, defaults to <code>{@link com.github.aliteralmind.templatefeather.GapCharConfig GapCharConfig}.{@link com.github.aliteralmind.templatefeather.GapCharConfig#DEFAULT_LITERAL_PREFIX DEFAULT_LITERAL_PREFIX}</code></p> * @see <code><a href="#tmpl_gaps"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></code> * @see #GAP_NAME_PREFIX_CHAR * @see #GAP_NAME_LITERAL_POSTFIX * @see #getGapCharConfig() */ public static final String GAP_NAME_LITERAL_PREFIX = "gap_literal_prefix_char"; /** <p>Literal representation of the postfix char, for use in between texts--Name is {@code "gap_literal_name_postfix_char"}.</p> <p>If not provided, defaults to <code>{@link com.github.aliteralmind.templatefeather.GapCharConfig GapCharConfig}.{@link com.github.aliteralmind.templatefeather.GapCharConfig#DEFAULT_LITERAL_PREFIX DEFAULT_LITERAL_PREFIX}</code></p> * @see <code><a href="#tmpl_gaps"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></code> * @see #GAP_NAME_POSTFIX_CHAR * @see #GAP_NAME_LITERAL_PREFIX * @see #getGapCharConfig() */ public static final String GAP_NAME_LITERAL_POSTFIX = "gap_literal_name_postfix_char"; /** <p>Fully-qualified name of the class that defines extra gaps that can be placed in Codelet templates--Name is {@code "user_extra_gaps_class"}.</p> <p>If there are no extra gaps, set this to the empty-string. Otherwise, the class it represents must {@linkplain java.lang.Class#forName(String) exist}, implement {@link UserExtraGapGetter}, and have a no-parameter constructor.</p> * @see <code><a href="#tmpl_gaps"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></code> * @see #DEFAULT_TPML_DIR_PREFIX * @see #getUserExtraGapsClassName() */ public static final String USER_EXTRA_GAPS_CLASS_NAME = "user_extra_gaps_class"; /** <p>Directory in which any custom (user-created) templates are stored--Name is {@code "user_template_base_dir"}.</p> <p>This is optional and not validated in any way. Its value may include the "{@link #BASE_DIR_BASE_DIR base_dir_base_dir}".</p> * @see <code><a href="#tmpl_gaps"><IMG SRC="{@docRoot}/resources/up_arrow.gif"/></a></code> * @see #getCustomTemplateDir() */ public static final String USER_TEMPLATE_BASE_DIR = "user_template_base_dir"; /** <p>Load configuration and get the instance. Call only once.</p> * @param props May not be {@code null}, and must be a valid {@linkplain CodeletBaseConfig Codelet properties} file. * @return #INSTANCE * @exception IllegalStateException If configuration was already loaded. * @see com.github.xbn.util.PropertiesUtil */ static final CodeletBaseConfig loadConfigGetInstance(Properties props, String config_baseDir, Iterator<String> externalJDDocRoot_configLineItr, Iterator<String> rqdDbgLevels_configLineItr, Iterator<String> debugLevels_configLineItr) throws NoSuchFileException, AccessDeniedException, MalformedURLException, IOException, InterruptedException { if (wasLoaded()) { throw new IllegalStateException("wasLoaded() is true."); } String dbgLvlStr = null; try { dbgLvlStr = props.getProperty(GLOBAL_DEBUG_LEVEL, ""); } catch (RuntimeException rx) { throw CrashIfObject.nullOrReturnCause(props, "props", null, rx); } dbgLevel = DebugLevel.getFromStringOff12345(dbgLvlStr, GLOBAL_DEBUG_LEVEL); String dbgAptrStr = props.getProperty(DEBUG_DESTINATION, ""); if (dbgAptrStr.equals("console")) { dbgAptr = TextAppenter.CONSOLE; doDebugToConsole = true; } else { dbgAptr = NewTextAppenterFor.file(dbgAptrStr, AppendOrOverwrite.APPEND); doDebugToConsole = false; } if (dbgLevel.isOn()) { debugln("Loading Codelet configuration"); debugln(" Loading base config"); debugln(" Loading debug level config file..."); } //Named debuggers cannet be used until the named-level map is created! int namedDebugLevelsRqd = 0; Map<String, DebugLevel> rqdDbgLevelNameMap = NamedDebuggers.newMapFromConfigFile(null, rqdDbgLevels_configLineItr, "rqdDbgLevels_configLineItr", null); //debug namedDebugLevelsRqd = rqdDbgLevelNameMap.size(); namedDebugLevels = new NamedDebuggers(rqdDbgLevelNameMap, debugLevels_configLineItr, "rqdDbgLevelNameMap", null); //debug //NOW named debuggers can be used. if (isDebugOn(null, "zzconfiguration.nameddebuglevels.listallafterload")) { debugln("All named debuggers:" + MapUtil.toString(namedDebugLevels.getMap(), null)); } namedDebugLevels.setAllQueriesDebug(getDebugApblIfOn(null, "zzconfiguration.nameddebuglevels.eachquery")); String baseDirBaseRplcmntQuoted = Matcher.quoteReplacement(props.getProperty(BASE_DIR_BASE_DIR, "")); xmplSrcBaseDir = get1stDollarBASERplcd(props.getProperty(EXAMPLE_CLASS_SRC_BASE_DIR, ""), baseDirBaseRplcmntQuoted); String enclosingBaseDirPropVal = props.getProperty(ENCLOSING_CLASS_SRC_BASE_DIRS, ""); enclosingBaseDirs = null; if (enclosingBaseDirPropVal.length() == 0) { throw new IllegalArgumentException( "No values for " + ENCLOSING_CLASS_SRC_BASE_DIRS + ". At least one required."); } else { enclosingBaseDirs = enclosingBaseDirPropVal.split(","); for (int i = 0; i < enclosingBaseDirs.length; i++) { enclosingBaseDirs[i] = get1stDollarBASERplcd(enclosingBaseDirs[i], baseDirBaseRplcmntQuoted); new PathMustBe().existing().readable().getOrCrashIfBad(enclosingBaseDirs[i], "Element " + i + " in " + ENCLOSING_CLASS_SRC_BASE_DIRS); } } enclosingBaseDirList = Collections.unmodifiableList(Arrays.asList(enclosingBaseDirs)); char gapNamePreChar = PropertiesUtil.getWithEmptyDefault(props, GAP_NAME_PREFIX_CHAR, Empty.OK, GapCharConfig.DEFAULT_PREFIX_CHAR); char gapNamePostChar = PropertiesUtil.getWithEmptyDefault(props, GAP_NAME_POSTFIX_CHAR, Empty.OK, GapCharConfig.DEFAULT_POSTFIX_CHAR); String gapLiteralPre = props.getProperty(GAP_NAME_LITERAL_PREFIX, GapCharConfig.DEFAULT_LITERAL_PREFIX); String gapLiteralPost = props.getProperty(GAP_NAME_LITERAL_POSTFIX, GapCharConfig.DEFAULT_LITERAL_POSTFIX); gapCharConfig = new GapCharConfig(gapNamePreChar, gapNamePostChar, gapLiteralPre, gapLiteralPost); String defaultAlterGetterClsNm = props.getProperty(DEFAULT_ALTERERS_CLASS_NAME, ""); if (defaultAlterGetterClsNm.length() == 0) { defaultAlterGetter = new DefaultDefaultAlterGetter(); if (isDebugOn(null, "zzconfiguration.progress")) { debugln(" No default-alter-getter class name provided. Using DefaultDefaultAlterGetter"); } } else { defaultAlterGetter = ReflectRtxUtil.<DefaultAlterGetter>getNewInstanceFromNoParamCnstr( defaultAlterGetterClsNm, DefaultAlterGetter.class, getDebugApblIfOn(null, "zzconfiguration.progress")); } String tmplBaseDir = props.getProperty(DEFAULT_TPML_DIR_PREFIX, ""); defaultSrcTmplPath = get1stDollarBASERplcd(tmplBaseDir + props.getProperty(DEFAULT_SRC_CODE_TMPL_PATH, ""), baseDirBaseRplcmntQuoted); defaultOutTmplPath = get1stDollarBASERplcd(tmplBaseDir + props.getProperty(DEFAULT_DOT_OUT_TMPL_PATH, ""), baseDirBaseRplcmntQuoted); defaultSrcAndOutTmplPath = get1stDollarBASERplcd( tmplBaseDir + props.getProperty(DEFAULT_AND_OUT_TMPL_PATH, ""), baseDirBaseRplcmntQuoted); defaultFileTextTmplPath = get1stDollarBASERplcd( tmplBaseDir + props.getProperty(DEFAULT_FILE_TEXT_TMPL_PATH, ""), baseDirBaseRplcmntQuoted); userExtraGapsClassNm = props.getProperty(USER_EXTRA_GAPS_CLASS_NAME, ""); customTmplDir = get1stDollarBASERplcd(props.getProperty(USER_TEMPLATE_BASE_DIR, ""), baseDirBaseRplcmntQuoted); blackWhiteList = FilenameBlackWhiteList.newFromProperties(props, ",", BLACK_WHITE_LIST_TYPE, BLACK_WHITE_LIST_CASE, BLACK_WHITE_PROPER_LIST, BLACK_WHITE_OVERRIDE_LIST, getDebugApblIfOn(null, "zzconfiguration.blackwhitelist.loading"), getDebugApblIfOn(null, "zzconfiguration.blackwhitelist.usage")); jdTgtClsMapInitCapacity = PropertiesUtil.getWithEmptyDefault(props, UNIQUE_JD_CLASS_TARGET_INIT_CAPACITY, Empty.OK, DEFAULT_JD_TARGET_CLASS_MAP_INIT_CAPACITY); doCrashIfAlterNotMade = PropertiesUtil.getWithEmptyDefault(props, ALTERATION_NOT_MADE_CRASH, "yes", "no", Empty.BAD, false); onlinePkgLstAttemptCount = PropertiesUtil.getWithEmptyDefault(props, PKGLIST_ONLINE_ATTEMPT_COUNT, Empty.OK, DEFAULT_ONLINE_PKGLST_ATTEMPTS); onlinePkgLstAttemptSleepMills = PropertiesUtil.getWithEmptyDefault(props, PKGLIST_ONLINE_ATTEMPT_SLEEP_MILLS, Empty.OK, DEFAULT_ONLINE_PKGLST_SLEEP_MILLS); doCrashIfOnlinePkgLstFails = PropertiesUtil.getWithEmptyDefault(props, PKGLIST_ONLINE_FAILS_BEHAVIOR, "crash", "warn", Empty.BAD, false); doAutoUpdateOfflinePkgLsts = PropertiesUtil.getWithEmptyDefault(props, AUTO_UPDATE_OFFLINE_PACKAGE_LISTS, "yes", "no", Empty.BAD, false); offlinePkgLstNamePost = props.getProperty(PKGLIST_OFFLINE_NAME_POSTFIX, ""); allDocRoots = AllOnlineOfflineDocRoots.newFromConfigLineIterator(externalJDDocRoot_configLineItr, config_baseDir + OFFLINE_PACKAGE_LIST_DIR_NAME + FILE_SEP, offlinePkgLstNamePost, onlinePkgLstAttemptCount, onlinePkgLstAttemptSleepMills, RefreshOffline.getForBoolean(doAutoUpdateOfflinePkgLsts), IfError.getCRASHIfTrue(doCrashIfOnlinePkgLstFails), getDebugApblIfOn(null, "zzconfiguration.progress"), getDebugAptr().getAppendable()); //Required!! if (isDebugOn(null, "zzconfiguration.allvaluessummary")) { debugln(" Codelet base-configuration loaded:"); debugln(" - " + EXAMPLE_CLASS_SRC_BASE_DIR + ": \"" + xmplSrcBaseDir + "\""); debugln(" - " + ENCLOSING_CLASS_SRC_BASE_DIRS + ": \"" + Arrays.toString(enclosingBaseDirList.toArray()) + "\""); debugln(" - " + DEFAULT_ALTERERS_CLASS_NAME + ": " + ((defaultAlterGetter == null) ? null : defaultAlterGetter.getClass().getName())); debugln(" Templates:"); debugln(" - Gap-name character configuration: " + gapCharConfig); debugln(" - " + USER_EXTRA_GAPS_CLASS_NAME + ": \"" + userExtraGapsClassNm + "\""); debugln(" - " + DEFAULT_TPML_DIR_PREFIX + ": \"" + tmplBaseDir + "\""); debugln(" - " + DEFAULT_SRC_CODE_TMPL_PATH + ": \"" + defaultSrcTmplPath + "\""); debugln(" - " + DEFAULT_DOT_OUT_TMPL_PATH + ": \"" + defaultOutTmplPath + "\""); debugln(" - " + DEFAULT_AND_OUT_TMPL_PATH + ": \"" + defaultSrcAndOutTmplPath + "\""); debugln(" - " + DEFAULT_FILE_TEXT_TMPL_PATH + ": \"" + defaultFileTextTmplPath + "\""); debugln(" - " + USER_TEMPLATE_BASE_DIR + ": \"" + customTmplDir + "\""); debugln(" JavaDoc:"); debugln(" - " + UNIQUE_JD_CLASS_TARGET_INIT_CAPACITY + ": " + jdTgtClsMapInitCapacity); debugln(" - " + PKGLIST_ONLINE_ATTEMPT_COUNT + ": " + onlinePkgLstAttemptCount); debugln(" - " + PKGLIST_ONLINE_ATTEMPT_SLEEP_MILLS + ": " + onlinePkgLstAttemptSleepMills); debugln(" - doCrashIfOnlinePackageListFailure(): " + doCrashIfOnlinePkgLstFails); debugln(" - doAutoUpdateOfflinePackageLists(): " + doAutoUpdateOfflinePkgLsts); debugln(" - doAutoUpdateOfflinePackageLists(): " + doAutoUpdateOfflinePkgLsts); debugln(" - doCrashIfAlterNotMade(): " + doCrashIfAlterNotMade); debugln(" - " + PKGLIST_OFFLINE_NAME_POSTFIX + ": \"" + offlinePkgLstNamePost + "\""); debugln(" - getAllJavaDocRoots(): " + allDocRoots); debugln(" Debugging:"); debugln(" - " + GLOBAL_DEBUG_LEVEL + ": " + dbgLevel); debugln(" - " + DEBUG_DESTINATION + ": " + dbgAptr); debugln(" - Black/white list: " + blackWhiteList); debugln(" - Named debuggers: " + namedDebugLevels.getMap().size() + " (" + namedDebugLevelsRqd + " required)"); debugln(" (done--ready to load CodeletTemplateConfig)"); } wasLoaded = true; return INSTANCE; } private static final String get1stDollarBASERplcd(String maybe_containsDollarBASE, String baseDirBase_rplcmntQuoted) { return dollarSignBASEMtchr.reset(maybe_containsDollarBASE).replaceFirst(baseDirBase_rplcmntQuoted); } private static final Matcher dollarSignBASEMtchr = Pattern.compile("${BASE}", Pattern.LITERAL).matcher(""); /** <p>Was configuration loaded?.</p> * @return {@code true} If all values loaded successfully. */ public static final boolean wasLoaded() { return wasLoaded; } /** <p>Path to the default template used for (source-code) {@code {@.codelet}} taglets.</p> * @see #DEFAULT_SRC_CODE_TMPL_PATH */ public static final String getDefaultSourceCodeTemplatePath() { return defaultSrcTmplPath; } /** <p>Path to the default template used for {@code {@.codelet.out}} taglets.</p> * @see #DEFAULT_DOT_OUT_TMPL_PATH */ public static final String getDefaultConsoleOutTemplatePath() { return defaultOutTmplPath; } /** <p>Path to the default template used for {@code {@.codelet.and.out}} taglets.</p> * @see #DEFAULT_AND_OUT_TMPL_PATH */ public static final String getDefaultSourceAndOutTemplatePath() { return defaultSrcAndOutTmplPath; } /** <p>Path to the default template used for {@code {@.file.textlet}} taglets.</p> * @see #DEFAULT_FILE_TEXT_TMPL_PATH */ public static final String getDefaultFileTextTemplatePath() { return defaultFileTextTmplPath; } /** <p>Write a message to the console and, if debugging is both on and <i>not</i> to the console, writes it via the debug outputter as well.</p> <p>This<ol> <li>Calls <code>System.out.println(message);</code></li> <li>If debugging is both {@linkplain #isDebugOn(CodeletInstance) on} and <i>not</i> {@linkplain #doDebugToConsole() to the console}, this also calls <br/> {@link #debugln(Object) debugln}{@code (message)}</li> </ol></p> * @param message <i>Should</i> not be {@code null} or empty. * @see #debugAndToConsole(CodeletInstance, Object) */ public static final void debuglnAndToConsole(CodeletInstance instance, Object message) { System.out.println(message); if (isDebugOn(instance) && !doDebugToConsole()) { debugln(message); } } /** <p>Write a message to the console and, if debugging is both on and <i>not</i> to the console, writes it via the debug outputter as well.</p> <p>This<ol> <li>Calls <code>System.out.print(message);</code></li> <li>If debugging is both {@linkplain #isDebugOn(CodeletInstance) on} and <i>not</i> {@linkplain #doDebugToConsole() to the console}, this also calls <br/> {@link #debug(Object) debug}{@code (message)}</li> </ol></p> * @param message <i>Should</i> not be {@code null} or empty. * @see #debuglnAndToConsole(CodeletInstance, Object) */ public static final void debugAndToConsole(CodeletInstance instance, Object message) { System.out.print(message); if (isDebugOn(instance) && !doDebugToConsole()) { debug(message); } } /** <p>Write a message to the debugging output.</p> * <p>Equal to <br/> <code>{@link #getDebugAptr() getDebugAptr}().{@link com.github.xbn.io.TextAppenter#appent(Object) appent}(message)</code></p> * @see #debugln(Object) */ public static final void debug(Object message) { getDebugAptr().appent(message); } /** <p>Write a message to the debugging output.</p> * <p>Equal to <br/> <code>{@link #getDebugAptr() getDebugAptr}().{@link com.github.xbn.io.TextAppenter#appentln(Object) appentln}(message)</code></p> * @see #debug(Object) */ public static final void debugln(Object message) { getDebugAptr().appentln(message); } /** <p>Is debugging output being written to the console?. This is used only to avoid {@linkplain #debuglnAndToConsole(CodeletInstance, Object) duplicate messages} when {@linkplain #isDebugOn(CodeletInstance) debugging is on}.</p> * @see #getGlobalDebugLevel() */ public static final boolean doDebugToConsole() { return doDebugToConsole; } /** <p>The amount of debugging, beyond ......logging......, that is written in every taglet call.</p> * @see #GLOBAL_DEBUG_LEVEL * @see #isDebugOn(CodeletInstance) isDebugOn(ci) * @see #isDebugOn(CodeletInstance, String) isDebugOn(ci,s) * @see #getDebugAptr() * @see #doDebugToConsole() * @see #debuglnAndToConsole(CodeletInstance, Object) log */ public static final DebugLevel getGlobalDebugLevel() { return dbgLevel; } /** <p>Is debugging on, either globally or for a single taglet?.</p> * @param instance May be {@code null}. * @return <code>{@link #getGlobalDebugLevel() getGlobalDebugLevel}().{@link com.github.xbn.io.DebugLevel#isThisOrAnyOn(DebugLevel...) isThisOrAnyOn}(instance.{@link CodeletInstance#getDebugLevel() getDebugLevel}())</code> * @see #isDebugOn(CodeletInstance, String) * @see #isDebugOn(int, CodeletInstance) */ public static final boolean isDebugOn(CodeletInstance instance) { return getGlobalDebugLevel().isThisOrAnyOn(getInstanceDbgLevelOrNull(instance)); } private static final DebugLevel getInstanceDbgLevelOrNull(CodeletInstance instance) { return ((instance == null) ? null : instance.getDebugLevel()); } /** <p>Is debugging on and at least a certain level?.</p> * @param instance May be {@code null}. * @return <code>{@link #getGlobalDebugLevel() getGlobalDebugLevel}().{@link com.github.xbn.io.DebugLevel#isHighestOnAndAtLeast(int, DebugLevel...) isHighestOnAndAtLeast}(min_level, instance.{@link CodeletInstance#getDebugLevel() getGlobalDebugLevel}())</code> * @see #isDebugOn(CodeletInstance) */ public static final boolean isDebugOn(int min_level, CodeletInstance instance) { //System.out.println("isDebugOn: global=" + getGlobalDebugLevel() + ", instance=" + getInstanceDbgLevelOrNull(instance) + ", highest-at-least-" + min_level + "=" + getGlobalDebugLevel().isHighestOnAndAtLeast(min_level, getInstanceDbgLevelOrNull(instance)) + ""); return getGlobalDebugLevel().isHighestOnAndAtLeast(min_level, getInstanceDbgLevelOrNull(instance)); } /** <p>Is debugging on and at most a certain level?.</p> * @param instance May be {@code null}. * @return <code>({@link #getGlobalDebugLevel() getGlobalDebugLevel}().{@link com.github.xbn.io.DebugLevel#getHighestNumber(DebugLevel...) getHighestNumber}(instance.{@link CodeletInstance#getDebugLevel() getGlobalDebugLevel}()) <= max_level)</code> */ public static final boolean isDebugLevelAtMost(int max_level, CodeletInstance instance) { //System.out.println("isDebugLevelAtMost: global=" + getGlobalDebugLevel() + ", instance=" + getInstanceDbgLevelOrNull(instance) + ", highest=" + getGlobalDebugLevel().getHighestNumber(getInstanceDbgLevelOrNull(instance)) + ", max_level=" + max_level + ", returning=" + (getGlobalDebugLevel().getHighestNumber(getInstanceDbgLevelOrNull(instance)) <= max_level) + ""); return (getGlobalDebugLevel().getHighestNumber(getInstanceDbgLevelOrNull(instance)) <= max_level); } /** <p>The debugging outputter.</p> * @see #DEBUG_DESTINATION * @see #getGlobalDebugLevel() * @see #getDebugApblIfOn(CodeletInstance) getDebugApblIfOn * @see #getDebugApblIfOn(int, CodeletInstance) getDebugApblIfOn * @see #doDebugToConsole() */ public static final TextAppenter getDebugAptr() { return dbgAptr; } /** <p>If debugging is on, get an appendable.</p> * @return <code>{@link #getDebugApblIfOn(int, CodeletInstance) getDebugApblIfOn}(0, instance)</code> */ public static final Appendable getDebugApblIfOn(CodeletInstance instance) { return getDebugApblIfOn(0, instance); } /** <p>If debugging is on, get an appenter.</p> * @return <code>{@link #getDebugAptrIfOn(int, CodeletInstance) getDebugAptrIfOn}(0, instance)</code> */ public static final TextAppenter getDebugAptrIfOn(CodeletInstance instance) { return getDebugAptrIfOn(0, instance); } /** <p>If debugging is on and at least a certain level, get an appendable.</p> * @return <code>(!{@link #isDebugOn(int, CodeletInstance) isDebugOn}(min_level, instance) ? null : {@link #getDebugAptr() getDebugAptr}().{@link com.github.xbn.io.TextAppenter#getAppendable() getAppendable}()))</code> * @see #getDebugApblIfOn(CodeletInstance) getDebugApblIfOn(ci) * @see #getDebugAptrIfOn(int, CodeletInstance) getDebugAptrIfOn(i,ci) */ public static final Appendable getDebugApblIfOn(int min_level, CodeletInstance instance) { return (!isDebugOn(min_level, instance) ? null : getDebugAptr().getAppendable()); } /** <p>If debugging is on and at least a certain level, get an appenter.</p> * @return <code>(!{@link #isDebugOn(int, CodeletInstance) isDebugOn}(min_level, instance) ? null : {@link #getDebugAptr() getDebugAptr}().{@link com.github.xbn.io.TextAppenter#getAppendable() getAppendable}()))</code> * @see #getDebugAptrIfOn(CodeletInstance) getDebugAptrIfOn(ci) * @see #getDebugApblIfOn(int, CodeletInstance) getDebugApblIfOn(i,ci) */ public static final TextAppenter getDebugAptrIfOn(int min_level, CodeletInstance instance) { return (!isDebugOn(min_level, instance) ? null : getDebugAptr()); } /** <p>Immutable map containing all named debug levels (both {@linkplain CodeletBootstrap#NAMED_DEBUGGERS_CONFIG_FILE required} and {@linkplain CodeletBootstrap#CODELET_RQD_NAMED_DBGRS_CONFIG_FILE user-created}).</p> * @see #isDebugOn(CodeletInstance, String) * @see #getDebugApblIfOn(CodeletInstance, String) getDebugApblIfOn(ci,s) * @see #getDebugAptrIfOn(CodeletInstance, String) getDebugAptrIfOn(ci,s) */ public static final NamedDebuggers getNamedDebuggers() { return namedDebugLevels; } /** <p>Is a named debuggers active?.</p> * @param instance May be {@code null}. * @return <code>{@link #getNamedDebuggers() getNamedDebuggers}().{@link com.github.aliteralmind.codelet.util.NamedDebuggers#isActive(String, DebugLevel, DebugLevel...) isActive}(name, <br/> {@link #getGlobalDebugLevel() getGlobalDebugLevel}(), instance.{@link CodeletInstance#getDebugLevel() getDebugLevel}()</code> * @see #getDebugApblIfOn(CodeletInstance, String) * @see #getDebugAptrIfOn(CodeletInstance, String) */ public static final boolean isDebugOn(CodeletInstance instance, String name) { return getNamedDebuggers().isActive(name, getGlobalDebugLevel(), getInstanceDbgLevelOrNull(instance)); } /** <p>If a named debuggers active, get an appendable.</p> * @param instance May be {@code null}. * @return <code>{@link #getNamedDebuggers() getNamedDebuggers}().{@link com.github.aliteralmind.codelet.util.NamedDebuggers#getAppendableIfActive(String, TextAppenter, DebugLevel, DebugLevel...) getAppendableIfActive}(name, <br/> {@link #getDebugAptr() getDebugAptr}(), {@link #getGlobalDebugLevel() getGlobalDebugLevel}(), <br/> instance.{@link CodeletInstance#getDebugLevel() getDebugLevel}()</code> * @see #getDebugAptrIfOn(CodeletInstance, String) */ public static final Appendable getDebugApblIfOn(CodeletInstance instance, String name) { return getNamedDebuggers().getAppendableIfActive(name, getDebugAptr(), getGlobalDebugLevel(), getInstanceDbgLevelOrNull(instance)); } /** <p>If a named debuggers active, get an appenter.</p> * @param instance May be {@code null}. * @return <code>{@link #getNamedDebuggers() getNamedDebuggers}().{@link com.github.aliteralmind.codelet.util.NamedDebuggers#getAppenterIfActive(String, TextAppenter, DebugLevel, DebugLevel...) getAppenterIfActive}(name, <br/> {@link #getDebugAptr() getDebugAptr}(), <br/> {@link #getGlobalDebugLevel() getGlobalDebugLevel}(), instance.{@link CodeletInstance#getDebugLevel() getDebugLevel}()</code> * @see #getDebugAptrIfOn(CodeletInstance, String) */ public static final TextAppenter getDebugAptrIfOn(CodeletInstance instance, String name) { return getNamedDebuggers().getAppenterIfActive(name, getDebugAptr(), getGlobalDebugLevel(), getInstanceDbgLevelOrNull(instance)); } /** <p>Directory containing the top-most package for the source code of all example classes.</p> * @see #EXAMPLE_CLASS_SRC_BASE_DIR */ public static final String getExampleSourceBaseDir() { return xmplSrcBaseDir; } /** <p>An immutable list of all directories containing the <i>top-most package</i> of codelet-containing source-code.</p> * @see #ENCLOSING_CLASS_SRC_BASE_DIRS */ public static final List<String> getEnclosingBaseDirList() { return enclosingBaseDirList; } static final String[] getEnclosingBaseDirs() { return enclosingBaseDirs; } /** <p>When <i>not</i> using a custom customizer (when using none, or a pre-made processor) how many spaces should all tabs be replaced with?.</p> * @see #DEFAULT_ALTERERS_CLASS_NAME */ public static final DefaultAlterGetter getDefaultAlterGetter() { return defaultAlterGetter; } /** <p>The character placed immediately before gap names.</p> * @see #GAP_NAME_POSTFIX_CHAR */ public static final GapCharConfig getGapCharConfig() { return gapCharConfig; } /** <p>The class that defines extra gaps that can be placed in Codelet templates.</p> * @see #USER_EXTRA_GAPS_CLASS_NAME */ public static final String getUserExtraGapsClassName() { return userExtraGapsClassNm; } /** <p>The directory in which any custom (user-created) templates are stored.</p> * @see #USER_TEMPLATE_BASE_DIR */ public static final String getCustomTemplateDir() { return customTmplDir; } /** <p>For determining which codelets should be processed or ignored.</p> * @see #BLACK_WHITE_LIST_TYPE */ public static final FilenameBlackWhiteList getBlackWhiteList() { return blackWhiteList; } public static final int getTargetClassMapInitCapacity() { return jdTgtClsMapInitCapacity; } /** <p>If a customizer attempts to make an alteration, but cannot (such as when the find-what text is not found in the example code), what should happen?.</p> * @return <ul> <li>{@code true}: If a warning should be logged and an exception should be thrown.</li> <li>{@code false}: If only a warning should be logged.</li> </ul> * @see #ALTERATION_NOT_MADE_CRASH */ public static final boolean doCrashIfAlterationNotMade() { return doCrashIfAlterNotMade; } /** <p>How many attempts should be made to retrieve each online package-list? If zero, offline only.</p> * @see #PKGLIST_ONLINE_ATTEMPT_COUNT */ public static final int getOnlinePackageListAttemptCount() { return onlinePkgLstAttemptCount; } /** <p>How many milliseconds between each failed attempt?.</p> * @see #PKGLIST_ONLINE_ATTEMPT_SLEEP_MILLS */ public static final long getOnlinePackageListAttemptSleepMills() { return onlinePkgLstAttemptSleepMills; } /** <p>If all online-retrieval attempts fail, should only a warning be logged, or should Codelet stop execution? (Offline package-lists are always retrieved.)</p> * @see #PKGLIST_ONLINE_FAILS_BEHAVIOR */ public static final boolean doCrashIfOnlinePackageListFailure() { return doCrashIfOnlinePkgLstFails; } /** <p>When online {@code package-list}s are retrieved, should offline package lists be refreshed (or created)?</p> * @see #AUTO_UPDATE_OFFLINE_PACKAGE_LISTS */ public static final boolean doAutoUpdateOfflinePackageLists() { return doAutoUpdateOfflinePkgLsts; } /** <p>The filename postfix, plus optional dot-extension that follows each offline name.</p> * @see #PKGLIST_OFFLINE_NAME_POSTFIX */ public static final String getOfflinePackageListPostfix() { return offlinePkgLstNamePost; } /** <p>All external JavaDoc doc roots, for <a href="{@docRoot}/overview-summary.html#xmpl_links">creating links</a>.</p> * @see #PKGLIST_ONLINE_ATTEMPT_COUNT */ public static final AllOnlineOfflineDocRoots getAllJavaDocRoots() { return allDocRoots; } }