001package Torello.JavaDoc.SyntaxHiLite; 002 003import Torello.Java.FileNode; 004import Torello.Java.StrPrint; 005import Torello.Java.FileRW; 006import Torello.Java.StorageWriter; 007 008// Needed for a JavaDoc Comment 009import Torello.Java.FileTransfer; 010 011import Torello.Java.CacheError; 012import Torello.Java.ToDoException; 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 * 023 * <BR /><BR /> 024 * <!-- <EMBED CLASS='external-html' DATA-FILE-ID=HLMC> --> 025 */ 026public class HiLiteCache64 027{ 028 // This is, as the name clearly says, the Cache-Directory 029 // 030 // This is now, August of 2024, officially "Package-Private" 031 // 032 // This is to allow ripping out some of the code from this class, and inserting it into 033 // "Helper Classes". The code in this class has always been very difficult to read, and 034 // creating separate, smaller, helper-classes makes it a lot easier. 035 // Unfortunately, this is now "Package-Private" instead of "Private". 036 037 final String cacheSaveDirectory; 038 039 // This is the list of Hash-Codes for all Code/HTML pairs stored in the cache. This is the 040 // exact data-structure that is referred to as the "Master Hash File" 041 // 042 // This has also been recently converted to "Package-Private", instead of "Private" 043 // See Above Note for Details. 044 045 final TreeSet<Long> hashCodes; 046 047 private static final short NUM_DIRS = 50; 048 049 /** 050 * Inform the user how much space (in bytes) is used by this {@code Cache}. 051 * @return The number of bytes being used on the file-system by this {@code Cache}. 052 */ 053 public long totalSize() 054 { 055 return FileNode 056 .createRoot(this.cacheSaveDirectory) 057 .loadTree() 058 .getDirTotalContentsSize(); 059 } 060 061 /** 062 * Count how many files and directories are contained in this {@code Cache}. 063 * @return The total number of files and sub-directories in the {@code Cache} directory. 064 */ 065 public int totalNumber() 066 { 067 return FileNode 068 .createRoot(this.cacheSaveDirectory) 069 .loadTree() 070 .count(); 071 } 072 073 /** 074 * This will load the hashCodes table to memory from the file-system directory identified 075 * by {@code String}-Parameter {@code 'cacheSaveDirectory'}. An exception shall be thrown 076 * if this file is not found. 077 * 078 * @param cacheSaveDirectory This constructor presumes that this cache has been used and 079 * visited before. This directory name should point to your local-cache of the 080 * {@code HiLite.ME} Server Code hilite past-operations. 081 * 082 * @throws CacheError This error will throw if the cache has not been instantiated, or 083 * is corrupted. If the specified directory does not exist, then this {@code Error} shall 084 * also throw. The chain-cause {@code Throwable} should be visible, and is included as the 085 * {@code Throwable.getCause()}. 086 */ 087 @LinkJavaSource(handle="CacheMethodsInternal64", name="checkCSD") 088 @LinkJavaSource(handle="CacheMethodsInternal64", name="checkTS") 089 public HiLiteCache64(String cacheSaveDirectory) throws CacheError 090 { 091 this.cacheSaveDirectory = CacheMethodsInternal64.checkCSD(cacheSaveDirectory); 092 this.hashCodes = CacheMethodsInternal64.checkTS(this.cacheSaveDirectory); 093 } 094 095 /** 096 * This will save the hash-code {@code TreeSet<Integer>} to disk. The <B>Master Hash-Code 097 * List</B> just keeps a record of the hashcodes of every {@code String} that was hilited 098 * by the Hiliter <I>(and therefore saved inside the Cache).</I> This method will save 099 * that Java {@code TreeSet} of Hash-Codes to disk. 100 * 101 * @throws CacheError This {@code Error} will throw if there is a problem writing the 102 * master cache-hash to disk. The chain-cause {@code Throwable} should be visible, and is 103 * included as the {@code Throwable.getCause()} 104 */ 105 @LinkJavaSource(handle="CacheMethodsInternal64", name="persistMasterHashToDisk") 106 public void persistMasterHashToDisk() throws CacheError 107 { CacheMethodsInternal64.persistMasterHashToDisk(this); } 108 109 /** 110 * Will write this method soon. It currently is not written. 111 * 112 * <BR /><BR /><B CLASS=JDDescLabel>TO DO:</B> 113 * 114 * <BR /> * 115 * This is supposed to be for "Error Recovery". Fortunately, an error has never really 116 * happend to me, and even if it did... Just deleting the whole thing and rebuilding 117 * the Cache by running the HiLiter on all of the files seems smarter/safer anyway. 118 * This has perpetually been on the "To Do List" for 5 years now... I think it more 119 * prudent to remind people, just delete and start over is probably smarter, it your 120 * Cache directory got messed up (for whatever reason - but mine never has anyway!) 121 */ 122 public void rebuildMasterHashCache() 123 { throw new ToDoException(); } 124 125 /** 126 * This will initialize a cache-file in the file-system directory identified by parameter 127 * {@code String cacheSaveDirectory}. If the directory specified does not exist, a 128 * {@code CacheError} is thrown. Any old cache files will be removed. To attempt to 129 * preserve old cache-files, call method {@code initializeOrRepair(String, StorageWriter)} 130 * 131 * <BR /><BR /><B><I>OrClear:</I></B> If the directory structure provided to this 132 * initialize method is not empty, the <SPAN STYLE="color: red;"><B><I>its entire contents 133 * shall be erased by a call to </I></B></SPAN> (Below) 134 * 135 * <DIV CLASS=LOC>{@code 136 * FileTransfer.deleteFilesRecursive 137 * (FileNode.createRoot(cacheSaveDirectory).loadTree(), sw); 138 * }</DIV> 139 * 140 * @param cacheSaveDirectory This constructor presumes that this cache has been used and 141 * visited before. This directory name should point to your local-cache of 142 * {@code HiLite.ME} Server Code hilite past-operations. 143 * 144 * @param sw This receives log-writes from the call to 145 * {@link FileTransfer#deleteFilesRecursive} which clears the files currently in the cache. 146 * This parameter may be null, and if it is, output-text will be shunted. 147 * 148 * @throws CacheError This exception will be throw if there are errors deleting any 149 * old-cache files currently in the directory; or if there is any error creating the new 150 * master hash-cache file. The chain-cause {@code Throwable} should be visible, and is 151 * included as the {@code Throwable.getCause()}. 152 */ 153 @LinkJavaSource(handle="CacheMethodsInternal64", name="initializeOrClear") 154 public static HiLiteCache64 initializeOrClear(String cacheSaveDirectory, StorageWriter sw) 155 throws CacheError 156 { return CacheMethodsInternal64.initializeOrClear(cacheSaveDirectory, sw); } 157 158 159 // ******************************************************************************************** 160 // ******************************************************************************************** 161 // Package-Private Methods 162 // ******************************************************************************************** 163 // ******************************************************************************************** 164 165 166 String get( 167 final String sourceCodeAsString, 168 final String codeTypeParam, 169 final boolean includeLineNumbers, 170 final byte styleNum 171 ) 172 { 173 final Long h = CacheMethodsInternal64.computeCacheKey 174 (codeTypeParam, includeLineNumbers, styleNum, sourceCodeAsString); 175 176 // NOTE: The Math.abs is OK, because it is just the directory name! (A little tricky) 177 if (! hashCodes.contains(h)) return null; 178 179 final String root = 180 cacheSaveDirectory + 181 StrPrint.zeroPad((Math.abs(h.intValue()) % NUM_DIRS)) + 182 File.separator + "H" + h.toString(); 183 184 185 // Does a try-catch & re-throw as CacheError, with appropriate Error-Message 186 // If the file is not found - PRECISELY BECAUSE THIS WAS A CACHE-HIT => ... 187 // ==> This would be a corrupted Cache. The method below throws 'CacheError' if there 188 // are any problems reading this file. 189 190 final String saved = readFile(root + "-SOURCE.sdat"); 191 192 // If the loaded Source-Code (from disk) - after a character for character comparison - is 193 // **EXACTLY EQUAL** to the Input / Requested Hi-Lite String, then the Hi-Lited Source-File 194 // is loaded from disk, and returned. 195 // 196 // CASE 1: 197 // If there is a Cache-Hit, followed by a successful "Equals Comparison", but then the 198 // HTML-File fails to load from disk - then (again) this must be a corrupted Cache and the 199 // method below will throw a Cache-Error if the file fails to load. 200 // 201 // CASE 2; 202 // if the "Equals Comparison" fails - the the HTML-File is an outdated File. This happens 203 // if-and-when the Textual-Changes slipped by without modifying the String.hashCode() 204 // computation. This is why the "Equals Comparison is mandatory". If the "equals" fails, 205 // then return null because the file will have to be re-hilited. 206 207 return saved.equals(sourceCodeAsString) 208 ? readFile(root + "-HILITE.sdat") 209 : null; 210 } 211 212 private static String readFile(final String fileName) 213 { 214 try 215 { return FileRW.readObjectFromFileNOCNFE(fileName, String.class, true); } 216 217 catch (Exception e) 218 { 219 throw new CacheError( 220 "There was an error reading a file from the cache-directory: " + 221 " [" + fileName + "]\n" + 222 "Please see cause throwable.getCause() for more details", 223 e 224 ); 225 } 226 } 227 228 void checkIn( 229 final String sourceCodeAsString, 230 final String hilitedCodeAsString, 231 final String codeTypeParam, 232 final boolean includeLineNumbers, 233 final byte styleNum 234 ) 235 { 236 final Long h = CacheMethodsInternal64.computeCacheKey 237 (codeTypeParam, includeLineNumbers, styleNum, sourceCodeAsString); 238 239 // NOTE: The Math.abs is OK, because it is just the directory name! (A little tricky) 240 final String root = cacheSaveDirectory + 241 StrPrint.zeroPad((Math.abs(h.intValue()) % NUM_DIRS)) + File.separator; 242 243 try 244 { 245 File f = new File(root); 246 if (! f.exists()) f.mkdirs(); 247 } 248 249 catch (Exception e) 250 { 251 throw new CacheError( 252 "There was an exception when accessing or creating the cache directory:\n" + 253 " [" + root + "...sdat].\n" + 254 "See cause exception Throwable.getCause() for details.", 255 e 256 ); 257 } 258 259 final String fileName = root + "H" + h.toString(); 260 261 try 262 { 263 FileRW.writeObjectToFile(sourceCodeAsString, fileName + "-SOURCE.sdat", true); 264 FileRW.writeObjectToFile(hilitedCodeAsString, fileName + "-HILITE.sdat", true); 265 } 266 267 catch (Exception e) 268 { 269 throw new CacheError( 270 "There was an exception when writing to the cache directory:\n" + 271 "[" + root + "...sdat].\n" + 272 "Attempting to write Files:\n" + 273 " [" + fileName + "-SOURCE.sdat].\n" + 274 " [" + fileName + "-HILITE.sdat].\n" + 275 "See cause exception Throwable.getCause() for details.", 276 e 277 ); 278 } 279 280 hashCodes.add(h); 281 // DEBUGING System.out.println(" CHECKEDIN "); 282 } 283 284 public static void main(String[] argv) 285 { 286 if (argv.length != 1) 287 { 288 System.out.println( 289 "argv.length != 1\n" + 290 "argv[0] must contain the Cache-Directory Name.\n" 291 ); 292 293 System.exit(1); 294 } 295 296 java.io.File f = new java.io.File(argv[0]); 297 298 if ((! f.exists()) || (! f.isDirectory())) 299 { 300 System.out.println( 301 "argv[0] must contain the Cache-Directory Name.\n" + 302 "Unfortunately: ((! f.exists()) || (! f.isDirectory()))\n" 303 ); 304 305 System.exit(1); 306 } 307 308 initializeOrClear(argv[0], new StorageWriter()); 309 } 310}