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 "&amp;#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 = "&#37;";
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/> &nbsp; &nbsp; <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/> &nbsp; &nbsp; {@code {@.codelet.out my.package.examples.AnExample((byte)-30, true, "Ribsy")%()}}
072      <br/>this parameter must equal
073      <br/> &nbsp; &nbsp; <code>my.package.examples.AnExample((byte)-30, true, "Ribsy"):()</code>
074      <br/>Get with {@link #getText() getText}{@code ()}. Any literal curly braces ({@code '&#123;}' or '{@code '&#125;}') found in string parameters are replaced with actual braces: {@code '&#123;'} and {@code '&#125;'}. 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("&#123;", "{").replace("&#125;", "}");
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/> &nbsp; &nbsp; <code>{@link java.lang.Class Class}.{@link java.lang.Class#forName(String) forName}({@link #getEnclosingFullyQualifiedName() getEnclosingFullyQualifiedName}())</code>
137      <br/>This returns
138      <br/> &nbsp; &nbsp; <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/> &nbsp; &nbsp; <code>getEnclosingPackage() + getEnclosingSimpleName()</code></li>
150         <li>One-or-more characters: <code>getEnclosingPackage() + &quot;.&quot; + 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}() + &quot;(&quot; + {@link #getLineNumber() getLineNumber}() &quot;): &quot; + {@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>&quot;{&amp;#64;&quot; + <i>[the-{@link #DEBUG_LEVEL_PREFIX_PREFIX debug-prefix}-if-provided]</i> + {@link #getType() getType}().{@link com.github.aliteralmind.codelet.CodeletType#getName() getName}() + &quot; &quot; + {@link #getText() getText}() + &quot;}&quot;</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}