001package Torello.Java.Additional;
002
003import Torello.JavaDoc.JDHeaderBackgroundImg;
004import Torello.Java.Verbosity;
005
006// Used for the JavaDoc '@see' tag, a few lines directly-below
007import Torello.HTML.Tools.Images.ImageScraper;
008
009/**
010 * Another Logging-Helper class.  This interface builds on the {@link AppendableSafe} class, and
011 * in this case it meaans that the internal {@code Appendable} that is wrapped by this one is an
012 * instance of {@link AppendableSafe} - <I>rather than the Standard-Java
013 * {@code java.lang.Appendable} interface</I>.
014 * 
015 * <BR /><BR />The purpose of the {@link AppendableSafe} class is merely to prevent Java's
016 * Checked-Exception {@code 'IOException'}, and replace it with any number of unchecked 
017 * {@code Throwable} options.  The purpose of this class is merely to add several convenience
018 * fields ({@link #hasLog} and {@link#level}) so that in the course of writing output-log code, a
019 * programmer is not bogged-down with 'Verbose Log Code' (Logging code that is, itself, extremely
020 * verbose-looking).
021 * 
022 * @see ImageScraper
023 * @see AppendableSafe
024 */
025@JDHeaderBackgroundImg(EmbedTagFileID={"APPENDABLE_EXTENSION", "APPENDABLE_LOG_JDHBI"})
026public class AppendableLog implements Appendable
027{
028    // ********************************************************************************************
029    // ********************************************************************************************
030    // Fields
031    // ********************************************************************************************
032    // ********************************************************************************************
033
034
035    /** Four space-characters. */
036    public static final String I4 = "    ";
037
038    /** Eight space-characters. */
039    public static final String I8 = I4 + I4;
040
041    /** Twelve space-characters. */
042    public static final String I12 = I8 + I4;
043
044    /** Sixteen space-characters. */
045    public static final String I16 = I8 + I8;
046
047    /**
048     * The actual log, itself.
049     * 
050     * <BR /><BR />This field may be passed and assigned null by the constructor.  The user should
051     * be aware to take some care if null log's are allowed in your code.  This just means that 
052     * there are circumstances where logging is to be "turned off".  In such cases, make sure to
053     * write code that chcks whether the log is null before printing to it.  (Make sure to avoid 
054     * throwing {@code NullPointerException})
055     * 
056     * <BR /><BR />This is an instance of {@link AppendableSafe} because the actual class
057     * {@code java.lang.Appendable} has methods all of which throw {@code IOException}.  The
058     * {@link AppendableSafe} class wraps those method calls and blocks the unchecked
059     * {@code IOException's}.
060     * 
061     * @see AppendableSafe
062     */
063    public final AppendableSafe log;
064
065    /**
066     * Simple way for the user to identify whether or not a null {@code 'appendable'} parameter was
067     * passed to this class Constructor.  If null was passed to {@link #log}, then this 
068     * {@code boolean} will be false.
069     */
070    public final boolean hasLog;
071
072    /** This is nothing more than the enum {@link Verbosity} field {@link Verbosity#level}. */
073    public final int level;
074
075
076    // ********************************************************************************************
077    // ********************************************************************************************
078    // Constructor
079    // ********************************************************************************************
080    // ********************************************************************************************
081
082
083    /**
084     * Construct an instance of this class.  The {@code 'appendable'} parameter, if non-null, is
085     * immediately wrapped into an {@link AppendableSafe} instance, and configured to throw 
086     * {@link AppendableError} rather than {@code IOException} when / if the internal method throws
087     * any {@code IOException's}.
088     * 
089     * @param appendable This may be any instance of {@code java.lang.Appendable}.
090     * 
091     * @param verbosity An instance of the {@link Verbosity} enum.  If parameter
092     * {@code 'appendable'} is null, then this paramter may also be null.  However if
093     * {@code 'appendable'} is non-null, then this may not be null either - or an exception shall
094     * throw.
095     * 
096     * @throws NullPointerException If parameter {@code 'appendable'} is non-null, but parameter
097     * {@code 'verbosity'} is null, then a {@code NullPointerException} will throw.
098     */
099    public AppendableLog(Appendable appendable, Verbosity verbosity)
100    {
101        if (appendable == null)
102        {
103            this.log    = null;
104            this.hasLog = false;
105            this.level  = 0;
106        }
107
108        else
109        {
110            this.log    = new AppendableSafe(appendable, AppendableSafe.USE_APPENDABLE_ERROR);
111            this.hasLog = true;
112            this.level  = verbosity.level;  // throws NPE
113        }
114    }
115
116    /**
117     * Construct an instance of this class.  Since there are several ways to configure the internal
118     * {@link AppendableSafe} instance, this constructor allows a user to simply pass an already
119     * built instance of {@code AppendableSafe} as a parameter.
120     * 
121     * @param appendableSafe This may be any instance of {@link AppendableSafe}.  The primary
122     * purpose of this class is to shunt the {@code IOException} that is thrown by
123     * {@code java.lang.Appendable} methods, and either suppress them, or re-wrap them into an
124     * unchecked-{@code Throwable}.
125     * 
126     * <BR /><BR />Being able to leave off the {@code throws IOException} when writing log
127     * code makes the process much easier.
128     * 
129     * @param verbosity An instance of the {@link Verbosity} enum.  If parameter
130     * {@code 'appendableSafe'} is null, then this paramter may also be null.  However if
131     * {@code 'appendableSafe'} is non-null, then this may not be null either - or an exception
132     * shall throw.
133     * 
134     * @throws NullPointerException If parameter {@code 'appendableSafe'} is non-null, but
135     * parameter {@code 'verbosity'} is null, then a {@code NullPointerException} will throw.
136     */
137    public AppendableLog(AppendableSafe appendableSafe, Verbosity verbosity)
138    {
139        if (appendableSafe == null)
140        {
141            this.log    = null;
142            this.hasLog = false;
143            this.level  = 0;
144        }
145
146        else
147        {
148            this.log    = appendableSafe;
149            this.hasLog = true;
150            this.level  = verbosity.level;  // throws NPE
151        }
152    }
153
154
155    // ********************************************************************************************
156    // ********************************************************************************************
157    // java.lang.Appendable Methods
158    // ********************************************************************************************
159    // ********************************************************************************************
160
161
162    /**
163     * Appends the specified character to this {@code Appendable}.
164     * 
165     * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class:
166     * {@code java.lang.Appendable}, <B>JDK 1.8</B></SPAN>
167     * 
168     * @param c The character to append
169     * @return A reference to this {@code Appendable}.
170     * 
171     * @throws NullPointerException If the {@code AppendableSafe} instance field ({@link #log}) was
172     * passed null during construction of {@code 'this'} instance.
173     */
174    public AppendableSafe append(char c)
175    { return this.log.append(c); }
176
177    /**
178     * Appends the specified character sequence to this {@code Appendable}.
179     * 
180     * <BR /><BR />Depending on which class implements the character sequence {@code 'csq'}, the
181     * entire sequence may not be appended.  For instance, if {@code 'csq'} is a
182     * {@code 'CharBuffer'} the subsequence to append is defined by the buffer's position and limit.
183     * 
184     * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class:
185     * {@code java.lang.Appendable}, <B>JDK 1.8</B></SPAN>
186     * 
187     * @param csq The character sequence to append. If csq is null, then the four characters "null"
188     * are appended to this {@code Appendable}.
189     * 
190     * @return A reference to this {@code Appendable}.
191     * 
192     * @throws NullPointerException If the {@code AppendableSafe} instance field ({@link #log}) was
193     * passed null during construction of {@code 'this'} instance.
194     */
195    public AppendableSafe append(CharSequence csq)
196    { return this.log.append(csq); }
197
198    /**
199     * Appends a subsequence of the specified character sequence to this {@code Appendable}.
200     * 
201     * <BR /><BR />An invocation of this method of the form {@code out.append(csq, start, end)}
202     * when {@code 'csq'} is not null, behaves in exactly the same way as the invocation:
203     * 
204     * <DIV CLASS=LOC>{@code
205     * out.append(csq.subSequence(start, end)) 
206     * }</DIV>
207     * 
208     * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class:
209     * {@code java.lang.Appendable}, <B>JDK 1.8</B></SPAN>
210     * 
211     * @param csq The character sequence from which a subsequence will be appended. If csq is null,
212     * then the four characters "null" are appended to this {@code Appendable}.
213     * 
214     * @param start The index of the first character in the subsequence
215     * @param end The index of the character following the last character in the subsequence
216     * 
217     * @return A reference to this {@code Appendable}.
218     * 
219     * @throws NullPointerException If the {@code AppendableSafe} instance field ({@link #log}) was
220     * passed null during construction of {@code 'this'} instance.
221     */
222    public AppendableSafe append(CharSequence csq, int start, int end)
223    { return this.log.append(csq, start, end); }
224
225
226    // ********************************************************************************************
227    // ********************************************************************************************
228    // More Appendable Methods
229    // ********************************************************************************************
230    // ********************************************************************************************
231
232
233    /**
234     * Appends four character spaces of indentation, and then appends {@code String}-parameter
235     * {@code's'}.
236     * 
237     * @return A reference to this {@code Appendable}.
238     * 
239     * @throws NullPointerException If the {@code AppendableSafe} instance field ({@link #log}) was
240     * passed null during construction of {@code 'this'} instance.
241     */
242    public AppendableSafe appendI4(String s)
243    { return this.log.append(I4).append(s); }
244
245    /**
246     * Appends eight character spaces of indentation, and then appends {@code String}-parameter
247     * {@code's'}.
248     * 
249     * @return A reference to this {@code Appendable}.
250     * 
251     * @throws NullPointerException If the {@code AppendableSafe} instance field ({@link #log}) was
252     * passed null during construction of {@code 'this'} instance.
253     */
254    public AppendableSafe appendI8(String s)
255    { return this.log.append(I8).append(s); }
256
257    /**
258     * Appends twelve character spaces of indentation, and then appends {@code String}-parameter
259     * {@code's'}.
260     * 
261     * @return A reference to this {@code Appendable}.
262     * 
263     * @throws NullPointerException If the {@code AppendableSafe} instance field ({@link #log}) was
264     * passed null during construction of {@code 'this'} instance.
265     */
266    public AppendableSafe appendI12(String s)
267    { return this.log.append(I12).append(s); }
268
269    /**
270     * Appends sixteen character spaces of indentation, and then appends {@code String}-parameter
271     * {@code's'}.
272     * 
273     * @return A reference to this {@code Appendable}.
274     * 
275     * @throws NullPointerException If the {@code AppendableSafe} instance field ({@link #log}) was
276     * passed null during construction of {@code 'this'} instance.
277     */
278    public AppendableSafe appendI16(String s)
279    { return this.log.append(I16).append(s); }
280
281
282    // ********************************************************************************************
283    // ********************************************************************************************
284    // Boolean-Test Methods
285    // ********************************************************************************************
286    // ********************************************************************************************
287
288
289    /**
290     * Quick check that can be used to test whether or not to print to a log.  The purpose of an
291     * instance of {@link Verbosity} is that logging information is only printed to an output log
292     * if the user has requested a certain level of verbosity.
293     * 
294     * <BR /><BR />This method will return {@code TRUE} if the user has requested logging, and
295     * this instance' internal {@link #level} field is equal to parameter {@code 'level'}.
296     * 
297     * @param level An integer that should correspond to the enum {@link Verbosity} internal field
298     * {@link Verbosity#level}.
299     * 
300     * @return {@code TRUE} if -
301     * 
302     * <BR /><BR /><UL CLASS=JDUL>
303     * <LI><B>{@link #hasLog}</B> is also {@code TRUE}</LI>
304     * <LI><B>{@link #level}</B> equals value passed to parameter {@code 'level'}</LI>
305     * </UL>
306     * 
307     * <BR />Returns {@code FALSE} otherwise
308     */
309    public boolean levelEQ(int level)
310    {
311        if (hasLog) return (this.level == level);
312        return false;
313    }
314
315    /**
316     * Quick check that can be used to test whether or not to print to a log.  The purpose of an
317     * instance of {@link Verbosity} is that logging information is only printed to an output log
318     * if the user has requested a certain level of verbosity.
319     * 
320     * <BR /><BR />This method will return {@code TRUE} if the user has requested logging, and
321     * this instance' internal {@link #level} field is greater than or equal to parameter
322     * {@code 'level'}.
323     * 
324     * @param level An integer that should correspond to the enum {@link Verbosity} internal field
325     * {@link Verbosity#level}.
326     * 
327     * @return {@code TRUE} if -
328     * 
329     * <BR /><BR /><UL CLASS=JDUL>
330     * 
331     * <LI> Field <B>{@link #hasLog}</B> is also {@code TRUE}</LI>
332     * 
333     * <LI> Field <B>{@link #level}</B> is greater than or equal to value passed to parameter
334     *      {@code 'level'}
335     *      </LI>
336     * </UL>
337     * 
338     * <BR />Returns {@code FALSE} otherwise
339     */
340    public boolean levelGTEQ(int level)
341    {
342        if (hasLog) return (this.level >= level);
343        return false;
344    }
345}