001package Torello.JavaDoc.SyntaxHiLite;
002
003import Torello.Java.FileNode;
004import Torello.Java.StrPrint;
005import Torello.Java.FileRW;
006import Torello.Java.StorageWriter;
007
008import Torello.Java.Function.QuadFunction;
009
010// Needed for a JavaDoc Comment
011import Torello.Java.FileTransfer;
012
013import Torello.JavaDoc.LinkJavaSource;
014
015import java.util.TreeSet;
016
017import java.io.File;
018
019/**
020 * A caching-system class that allows this tool to efficiently bypass calls to the server when
021 * an exact-copy of the hilited source-code already exists inside the cache.
022 */
023public abstract class AbstractHashCodeHLC<NUMBER extends Number> implements HiLiteCache
024{
025    static final short NUM_DIRS = 5000;
026
027    static
028    {
029        if ((NUM_DIRS < 1000) || (NUM_DIRS > 9999))
030            throw new InternalError("Wrong Number of NUM_DIRS");
031    }
032
033
034    // ********************************************************************************************
035    // ********************************************************************************************
036    // Instance Fields
037    // ********************************************************************************************
038    // ********************************************************************************************
039
040
041    /** This is nothing more than a "reified" Generic Type Parameter.  No more, no less. */
042    public final Class<NUMBER> NUMBER_CLASS;
043
044    /** This is, as the name clearly says, the Cache's Storae-Directory */
045    public final String cacheSaveDirectory;
046
047
048    // This is the list of Hash-Codes for all Code/HTML pairs stored in the cache.  This is the
049    // exact data-structure that is referred to as the "Master Hash File"
050    // 
051    // This has also been recently converted to "Package-Private", instead of "Private"
052    // See Above Note for Details.
053
054    final TreeSet<NUMBER> hashCodes;
055
056
057    // ********************************************************************************************
058    // ********************************************************************************************
059    // Constructor
060    // ********************************************************************************************
061    // ********************************************************************************************
062
063
064    /**
065     * This will load the hashCodes table to memory from the file-system directory identified
066     * by {@code String}-Parameter {@code 'cacheSaveDirectory'}.  An exception shall be thrown
067     * if this file is not found.
068     *
069     * @param cacheSaveDirectory This constructor presumes that this cache has been used and
070     * visited before.  This directory name should point to your local-cache of the
071     * {@code HiLite.ME} Server Code hilite past-operations.
072     *
073     * @throws CacheError This error will throw if the cache has not been instantiated, or
074     * is corrupted.  If the specified directory does not exist, then this {@code Error} shall
075     * also throw.  The chain-cause {@code Throwable} should be visible, and is included as the 
076     * {@code Throwable.getCause()}.
077     */
078    @LinkJavaSource(handle="TreeSetMethods", name="checkCSD")
079    @LinkJavaSource(handle="TreeSetMethods", name="checkTS")
080    public AbstractHashCodeHLC(
081            final String        cacheSaveDirectory,
082            final Class<NUMBER> NUMBER_CLASS
083        )
084    {
085        this.cacheSaveDirectory = TreeSetMethods.checkCSD(cacheSaveDirectory);
086        this.hashCodes          = TreeSetMethods.checkTS(this.cacheSaveDirectory, NUMBER_CLASS);
087        this.NUMBER_CLASS       = NUMBER_CLASS;
088    }
089
090
091    // ********************************************************************************************
092    // ********************************************************************************************
093    // Static Builder Method
094    // ********************************************************************************************
095    // ********************************************************************************************
096
097
098    /**
099     * This will initialize a cache-file in the file-system directory identified by parameter
100     * {@code String cacheSaveDirectory}.  If the directory specified does not exist, a
101     * {@code CacheError} is thrown.  Any old cache files will be removed.  To attempt to
102     * preserve old cache-files, call method {@code initializeOrRepair(String, StorageWriter)}
103     * 
104     * <BR /><BR /><B><I>OrClear:</I></B> If the directory structure provided to this
105     * initialize method is not empty, the <SPAN STYLE="color: red;"><B><I>its entire contents
106     * shall be erased by a call to </I></B></SPAN> (Below)
107     * 
108     * <DIV CLASS=LOC>{@code 
109     * FileTransfer.deleteFilesRecursive
110     *     (FileNode.createRoot(cacheSaveDirectory).loadTree(), sw);
111     * }</DIV>
112     * 
113     * @param cacheSaveDirectory This constructor presumes that this cache has been used and
114     * visited before.  This directory name should point to your local-cache of 
115     * {@code HiLite.ME} Server Code hilite past-operations.
116     * 
117     * @param sw This receives log-writes from the call to
118     * {@link FileTransfer#deleteFilesRecursive} which clears the files currently in the cache.
119     * This parameter may be null, and if it is, output-text will be shunted.
120     * 
121     * @throws CacheError This exception will be throw if there are errors deleting any
122     * old-cache files currently in the directory; or if there is any error creating the new
123     * master hash-cache file.  The chain-cause {@code Throwable} should be visible, and is 
124     * included as the {@code Throwable.getCause()}.
125     */
126    @LinkJavaSource(handle="TreeSetMethods", name="initializeOrClear")
127    public static String initializeOrClear(String cacheSaveDirectory, StorageWriter sw)
128    { return TreeSetMethods.initializeOrClear(cacheSaveDirectory, sw); }
129
130
131    // ********************************************************************************************
132    // ********************************************************************************************
133    // Implemented Interface Methods
134    // ********************************************************************************************
135    // ********************************************************************************************
136
137
138    public long totalSize()
139    {
140        return FileNode
141            .createRoot(this.cacheSaveDirectory)
142            .loadTree()
143            .getDirTotalContentsSize();
144    }
145
146    public int totalNumber()
147    {
148        return FileNode
149            .createRoot(this.cacheSaveDirectory)
150            .loadTree()
151            .count();
152    }
153
154    @LinkJavaSource(handle="TreeSetMethods", name="persistMasterHashToDisk")
155    public void persistMasterHashToDisk() throws CacheError
156    { TreeSetMethods.persistMasterHashToDisk(this.hashCodes, this.cacheSaveDirectory); }
157
158
159    // ********************************************************************************************
160    // ********************************************************************************************
161    // Implemented Interface Methods: Check In and Out
162    // ********************************************************************************************
163    // ********************************************************************************************
164
165
166    @LinkJavaSource(handle="CheckInOut", name="get")
167    public String get(
168            final String    sourceCodeAsString,
169            final String    codeTypeParam,
170            final boolean   includeLineNumbers,
171            final byte      styleNum
172        )
173    {
174        return CheckInOut.get(
175            sourceCodeAsString,
176            this.computeCacheKey
177                (codeTypeParam, includeLineNumbers, styleNum, sourceCodeAsString),
178            this
179        );
180    }
181
182    @LinkJavaSource(handle="CheckInOut", name="checkIn")
183    public void checkIn(
184            final String    sourceCodeAsString,
185            final String    hilitedCodeAsString, 
186            final String    codeTypeParam,
187            final boolean   includeLineNumbers,
188            final byte      styleNum
189        )
190    {
191        CheckInOut.checkIn(
192            sourceCodeAsString,
193            hilitedCodeAsString,
194            this.computeCacheKey
195                (codeTypeParam, includeLineNumbers, styleNum, sourceCodeAsString),
196            this 
197        );
198    }
199
200
201    // ********************************************************************************************
202    // ********************************************************************************************
203    // Abstract Methods
204    // ********************************************************************************************
205    // ********************************************************************************************
206
207
208    /**
209     * Compute a Hash-Code
210     * @param codeTypeParam         <EMBED CLASS='external-html' DATA-FILE-ID=HLC_PARAM_CODE_TP>
211     * @param includeLineNumbers    <EMBED CLASS='external-html' DATA-FILE-ID=HLC_PARAM_INC_LINEN>
212     * @param styleNum              <EMBED CLASS='external-html' DATA-FILE-ID=HLC_PARAM_STYLE_N>
213     * @param sourceCodeAsString    <EMBED CLASS='external-html' DATA-FILE-ID=HLC_PARAM_SRC_ASSTR>
214     * @return A key which may be used for saving a file to disk.
215     */
216    public abstract NUMBER computeCacheKey(
217            final String    codeTypeParam,
218            final boolean   includeLineNumbers,
219            final byte      styleNum,
220            final String    sourceCodeAsString
221        );
222
223    abstract String getSubDirName(NUMBER hashCode);
224
225
226    // ********************************************************************************************
227    // ********************************************************************************************
228    // I forgot ...
229    // ********************************************************************************************
230    // ********************************************************************************************
231
232
233    public static void main(String[] argv)
234    {
235        if (argv.length != 1)
236        {
237            System.out.println(
238                "argv.length != 1\n" +
239                "argv[0] must contain the Cache-Directory Name.\n"
240            );
241
242            System.exit(1);
243        }
244
245        java.io.File f = new java.io.File(argv[0]);
246
247        if ((! f.exists()) || (! f.isDirectory()))
248        {
249            System.out.println(
250                "argv[0] must contain the Cache-Directory Name.\n" +
251                "Unfortunately: ((! f.exists()) || (! f.isDirectory()))\n"
252            );
253
254            System.exit(1);
255        }
256
257        initializeOrClear(argv[0], new StorageWriter());
258    }
259}