001/*license*\ 002 Codelet: Copyright (C) 2014, Jeff Epstein (aliteralmind __DASH__ github __AT__ yahoo __DOT__ com) 003 004 This software is dual-licensed under the: 005 - Lesser General Public License (LGPL) version 3.0 or, at your option, any later version; 006 - Apache Software License (ASL) version 2.0. 007 008 Either license may be applied at your discretion. More information may be found at 009 - http://en.wikipedia.org/wiki/Multi-licensing. 010 011 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: 012 - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt 013 - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt 014\*license*/ 015package com.github.aliteralmind.codelet; 016 import com.github.xbn.io.DebugLevel; 017 import com.github.xbn.lang.reflect.ReflectRtxUtil; 018 import java.io.File; 019 import java.util.Objects; 020/** 021 <p>Represents a single codelet-taglet, as found by {@code javadoc.exe}. Contains its basic elements that compose a single codelet: Its name, text, and enclosing class. Also contains the relative url to the JavaDoc root directory ({@code "{@docRoot}"}), and utility functions.</p> 022 023 @see com.github.aliteralmind.codelet.taglet.CodletComSunJavadocTagProcessor#get(Tag) taglet.CodletComSunJavadocTagProcessor#get(Tag) 024 * @since 0.1.0 025 * @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> 026 **/ 027public class CodeletInstance { 028 private final DebugLevel dbgLvl ; 029 private final String enclosingPackage ; 030 private final String enclosingSimpleNm; 031 private final File enclosingFile ; 032 private final int lineNum ; 033 private final String text ; 034 private final String relUrlToDocRoot ; 035 private final CodeletType type ; 036 /** 037 <p>The prefix that, if found before a taglet's text, turns on debugging for that taglet only--Equal to {@code "[DEBUG_LEVEL_"}. This overrides {@linkplain CodeletBaseConfig#GLOBAL_DEBUG_LEVEL global debugging}, but only if the taglet level is higher.</p> 038 039 <p>Example:</p> 040 041<blockquote>{@code [DEBUG_LEVEL_3]{@.codelet com.github.myjavacode.examples.AnExample}}</blockquote> 042 043 <p>If {@linkplain CodeletBaseConfig#getGlobalDebugLevel() global debugging} is <code>{@link com.github.xbn.io.DebugLevel#OFF OFF}</code>, then this taglet outputs all 1, 2, and 3-level debugging statements. 044 */ 045 public static final String DEBUG_LEVEL_PREFIX_PREFIX = "[DEBUG_LEVEL_"; 046 /** 047 <p>The character that divides the fully-qualified class-name or plain-text file-path, and its optional <a href="CustomizationInstructions.html#overview">Codelet-customizer</a>--Equal to {@code '%'}.</p> 048 049 <p>If there is no customizer, this character must not exist in the taglet's text. For literally-displaying this character (in a string-value parameter, for either the processor or the example code's {@code main} function), use {@link #ESCAPED_CUSTOMIZER_PREFIX_CHAR}.</p> 050 051 <p><i>This cannot be a colon, because it conflicts with absolute Windows paths (such as {@code C:\java_code\}).</i></p> 052 */ 053 public static final char CUSTOMIZER_PREFIX_CHAR = '%'; 054 /** 055 <p>When the customizer prefix-char needs to be literally displayed in a string-value parameter--Equal to {@code "&#37;"}.</p> 056 057 * @see #CUSTOMIZER_PREFIX_CHAR 058 * @see com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature#getObjectFromString(String) 059 */ 060 public static final String ESCAPED_CUSTOMIZER_PREFIX_CHAR = "%"; 061 /** 062 <p>Create a new Codelet from its taglet elements.</p> 063 064 * @param taglet_name The {@linkplain CodeletType#getName() name} of the taglet, which implies its type. Get with {@link #getType() getType}{@code ()}. 065 * @param enclosing_package The fully-qualified class name of the codelet's enclosing class--the class whose source-code contains a JavaDoc block, in which this taglet exists. This must be an actually-existing class, as determined by 066 <br/> <code>{@link java.lang.Class Class}.{@link java.lang.Class#forName(String) forName}(enclosing_package)</code>. Get with {@link #getEnclosingPackage() getEnclosingPackage}{@code ()}. 067 * @param enclosing_simpleName The name of the enclosing file, without any path (or {@linkplain com.github.aliteralmind.codelet.taglet.ComSunJavaDocUtil#getEnclosingSimpleName(Tag, IncludePostClassName) generics or function signature}). If the containing file is the overview summary, this must equal {@code "OVERVIEW_SUMMARY"}. If a package summary, this must be {@code "PACKAGE_SUMMARY"}. 068 * @param enclosing_file The enclosing file's {@code File} object, which contains the full path to its source code. 069 * @param line_num The line number in the enclosing classes source code, at which this taglet exists. May not be less than one. Get with {@link #getLineNumber() getLineNumber}{@code ()}. 070 * @param tag_text The original text following the taglet's name. For example, if the taglet is 071 <br/> {@code {@.codelet.out my.package.examples.AnExample((byte)-30, true, "Ribsy")%()}} 072 <br/>this parameter must equal 073 <br/> <code>my.package.examples.AnExample((byte)-30, true, "Ribsy"):()</code> 074 <br/>Get with {@link #getText() getText}{@code ()}. Any literal curly braces ({@code '{}' or '{@code '}}') found in string parameters are replaced with actual braces: {@code '{'} and {@code '}'}. Escaping curlys is required in order to prevent {@code javadoc.exe} from incorrectly parsing taglets. 075 * @param relUrl_toDocRoot The relative directory from the containing JavaDoc file to the JavaDoc root directory (the value of {@code {@docRoot}}). Get with {@link #getRelativeUrlToDocRoot() getRelativeUrlToDocRoot}{@code ()}. 076 */ 077 public CodeletInstance(String taglet_name, String enclosing_package, String enclosing_simpleName, File enclosing_file, int line_num, String tag_text, String relUrl_toDocRoot) { 078 Objects.requireNonNull(relUrl_toDocRoot, "relUrl_toDocRoot"); 079 if(enclosing_simpleName.indexOf('<') != -1 || 080 enclosing_simpleName.indexOf('(') != -1) { 081 throw new IllegalArgumentException("enclosing_simpleName (\"" + enclosing_simpleName + "\") contains an open parenthsis ('(') or greater-than sign ('<')."); 082 } 083 084 if(!tag_text.startsWith(DEBUG_LEVEL_PREFIX_PREFIX)) { 085 dbgLvl = DebugLevel.OFF; 086 087 } else { 088 int prePreLen = DEBUG_LEVEL_PREFIX_PREFIX.length(); 089 String levelDigitStr = null; 090 try { 091 levelDigitStr = tag_text.substring(prePreLen, (prePreLen + 1)); 092 } catch(StringIndexOutOfBoundsException sbx) { 093 throw new IllegalArgumentException("Text *equals* \"" + DEBUG_LEVEL_PREFIX_PREFIX + "\"", sbx); 094 } 095 dbgLvl = DebugLevel.getFromStringOff12345(levelDigitStr, "[Codelet taglet prefix level number]"); 096 097 try { 098 tag_text = tag_text.substring(tag_text.indexOf("]") + 1); 099 } catch(StringIndexOutOfBoundsException sbx) { 100 throw new IllegalArgumentException("Text starts with \"" + DEBUG_LEVEL_PREFIX_PREFIX + "\", but does not contain the close ']'.", sbx); 101 } 102 } 103 104 type = CodeletType.newTypeForTagletName(taglet_name, "taglet_name"); 105 106 text = tag_text.replace("{", "{").replace("}", "}"); 107 108 enclosingPackage = enclosing_package; 109 enclosingSimpleNm = enclosing_simpleName; 110 enclosingFile = enclosing_file; 111 112 lineNum = line_num; 113 relUrlToDocRoot = relUrl_toDocRoot; 114 } 115 /** 116 <p>The original, unaltered {@linkplain com.sun.javadoc.Tag#text() text}, as found after the taglet's name (and any whitespace).</p> 117 118 * @see #CodeletInstance(String, String, String, File, int, String, String) CodeletInstance(s,s,s,f,i,s,s) 119 */ 120 public String getText() { 121 return text; 122 } 123 /** 124 <p>The type of this Codelet, which implies its {@linkplain CodeletType#getName() name}.</p> 125 126 * @see com.sun.javadoc.Tag#name() 127 * @see #CodeletInstance(String, String, String, File, int, String, String) CodeletInstance(s,s,s,f,i,s,s) 128 */ 129 public CodeletType getType() { 130 return type; 131 } 132 /** 133 <p>Get the taglet's enclosing class object, if it is a class.</p> 134 135 * @return If the enclosing JavaDoc file represents a class according to 136 <br/> <code>{@link java.lang.Class Class}.{@link java.lang.Class#forName(String) forName}({@link #getEnclosingFullyQualifiedName() getEnclosingFullyQualifiedName}())</code> 137 <br/>This returns 138 <br/> <code>{@link com.github.xbn.lang.reflect.ReflectRtxUtil#getClassIfExistsOrNull(String) getClassIfExistsOrNull}(getEnclosingFullyQualifiedName())</code> 139 <br/>Otherwise, this returns {@code null}. 140 */ 141 public Class<?> getEnclosingClass() { 142 return ReflectRtxUtil.getClassIfExistsOrNull(getEnclosingFullyQualifiedName()); 143 } 144 /** 145 <p>Get the fully-qualified name of the taglet's enclosing JavaDoc file. If it is a class, this is appropriate for <code>{@link java.lang.Class}.{@link java.lang.Class#forName(String) forName})</code>.</p> 146 147 * @return If {@link #getEnclosingPackage() getEnclosingPackage}{@code ()} has<ul> 148 <li>zero characters: 149 <br/> <code>getEnclosingPackage() + getEnclosingSimpleName()</code></li> 150 <li>One-or-more characters: <code>getEnclosingPackage() + "." + getEnclosingSimpleName()</code></li> 151 </ul> 152 * @see #getEnclosingClass() 153 */ 154 public String getEnclosingFullyQualifiedName() { 155 StringBuilder bldr = new StringBuilder(getEnclosingPackage()); 156 if(getEnclosingPackage().length() > 0) { 157 bldr.append("."); 158 } 159 return bldr.append(getEnclosingSimpleName()).toString(); 160 } 161 /** 162 <p>The package name of this taglet's {@linkplain com.sun.javadoc.Tag#holder() containing JavaDoc file}.</p> 163 164 * @see #isOverviewSummary() 165 * @see #getEnclosingClass() 166 * @see #getEnclosingFullyQualifiedName() 167 * @see #getEnclosingSimpleName() 168 * @see #getEnclosingFile() 169 * @see #getEnclosingPackage() 170 * @see #CodeletInstance(String, String, String, File, int, String, String) CodeletInstance(s,s,s,f,i,s,s) 171 */ 172 public String getEnclosingPackage() { 173 return enclosingPackage; 174 } 175 /** 176 <p>Is the enclosing file the <a href="http://docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/javadoc.html#overview">overview summary</a> page?.</p> 177 178 * @return <code>({@link #getEnclosingPackage() getEnclosingPackage}().length() == 0)</code> 179 */ 180 public boolean isOverviewSummary() { 181 return (getEnclosingPackage().length() == 0); 182 } 183 /** 184 <p>The post-package name of this taglet's containing JavaDoc file.</p> 185 186 * @see #getEnclosingPackage() 187 */ 188 public String getEnclosingSimpleName() { 189 return enclosingSimpleNm; 190 } 191 /** 192 <p>The {@linkplain com.sun.javadoc.SourcePosition#file() file-object} whose source code contains a JavaDoc block, in which this codelet exists. This contains the full path to the enclosing classes source code.</p> 193 194 * @see #getEnclosingPackage() 195 * @see #CodeletInstance(String, String, String, File, int, String, String) CodeletInstance(s,s,s,f,i,s,s) 196 */ 197 public File getEnclosingFile() { 198 return enclosingFile; 199 } 200 /** 201 <p>The {@linkplain com.sun.javadoc.SourcePosition#line() line number} in the enclosing classes source-code, at which this codelet exists.</p> 202 203 * @see #CodeletInstance(String, String, String, File, int, String, String) CodeletInstance(s,s,s,f,i,s,s) 204 */ 205 public int getLineNumber() { 206 return lineNum; 207 } 208 /** 209 <p>The relative directory from the containing classes JavaDoc file, to the JavaDoc root directory--Equivalent to {@code {@docRoot}}.</p> 210 211 * @see #CodeletInstance(String, String, String, File, int, String, String) CodeletInstance(s,s,s,f,i,s,s) 212 */ 213 public String getRelativeUrlToDocRoot() { 214 return relUrlToDocRoot; 215 } 216 /** 217 <p>The debugging level for this taglet only, as defined in the optional override prefix. This is used only when it is at a higher level than {@linkplain CodeletBaseConfig#getGlobalDebugLevel() global debugging}.</p> 218 219 * @see #DEBUG_LEVEL_PREFIX_PREFIX 220 * @see CodeletBaseConfig#isDebugOn(CodeletInstance) CodeletBaseConfig#isDebugOn 221 * @see CodeletBaseConfig#isDebugOn(CodeletInstance) CodeletBaseConfig#isDebugOn 222 */ 223 public DebugLevel getDebugLevel() { 224 return dbgLvl; 225 } 226 /** 227 <p>The enclosing file, plus line number, followed by the full taglet text.</p> 228 229 * @return <code>{@link #getEnclosingFile() getEnclosingFile}() + "(" + {@link #getLineNumber() getLineNumber}() "): " + {@link #getFullOriginalTaglet() getFullOriginalTaglet}()</code> 230 */ 231 public String toString() { 232 return getEnclosingFile() + "(" + getLineNumber() + "): " + getFullOriginalTaglet(); 233 } 234 /** 235 <p>Returns the full original taglet, exactly as found in the JavaDoc.</p> 236 237 * @return <code>"{&#64;" + <i>[the-{@link #DEBUG_LEVEL_PREFIX_PREFIX debug-prefix}-if-provided]</i> + {@link #getType() getType}().{@link com.github.aliteralmind.codelet.CodeletType#getName() getName}() + " " + {@link #getText() getText}() + "}"</code> 238 */ 239 public String getFullOriginalTaglet() { 240 StringBuilder bldr = new StringBuilder("{@").append(getType().getName()).append(" "); 241 if(getDebugLevel().isOn()) { 242 bldr.append(DEBUG_LEVEL_PREFIX_PREFIX). 243 append(getDebugLevel().getNumber()).append("]"); 244 } 245 return bldr.append(getText()).append("}").toString(); 246 } 247}