/* * [Path.java] * * Summary: Displays a fully qualified directory or file name. * * Copyright: (c) 2014-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2014-05-04 initial version */ package com.mindprod.htmlmacros.macro; import com.mindprod.common18.Build; import com.mindprod.common18.EIO; import com.mindprod.common18.Misc; import com.mindprod.common18.ST; import com.mindprod.csv.CSVWriter; import com.mindprod.fastcat.FastCat; import com.mindprod.htmlmacros.support.JDKandJREVersions; import java.io.File; import java.io.IOException; import java.util.regex.Pattern; import static java.lang.System.*; /** * Displays a fully qualified directory or file name. *

* Use macro Qualified for qualified Java class/method/var names. * * @author Roedy Green, Canadian Mind Products * @version 1.0 2014-05-04 initial version * @see Qualified * @since 2014-05-04 */ public final class Path extends Macro { // declarations /** * prefix for warning messages */ private static final String WARNING_PREFIX = "\n>>> Warning: macro Path: "; /** * directory names ok to contain dots without comment, case-sensitive */ private static final String[] DIRS_WITH_DOTS_OK = { "..", ".default", ".idea", ".IntelliJIdea2017.3", "1.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0", "10.0", "11.0", "12.0", "13.0", "14.0", "All Users.WINNT", "Bradsoft.com", "Dwww.mindprod.com", Build.INTELLIJ_DIR, JDKandJREVersions.JDK_FULL_VERSION, Build.JET_PROFILE_VERSION, Build.JET_FULL_VERSION, "KompoZer 0.7.10", "OpenOffice.org", "RhinoSoft.com", "Windows.old", "apache-ant-1.10.3", "jaf-1.1.1", "javamail-1.5.6", "Microsoft.MicrosoftEdge_8wekyb3d8bbwe", "tomcat-8.5.24", "wincvs 1.2", "winzip 21.0" }; /** * where website files are kept */ private static final File webrootDir = new File( Global.configuration.getLocalWebrootWithSlashes() ); /** * split of / and \ but not . */ private static final Pattern FILE_SPLITTER = Pattern.compile( "(?!/var>)/|\\\\" ); /** * how to use the macro */ private static final String USAGE = "\nQualified macro needs {pathname} [quoted] (directories have a trailing \\ or /)"; /** * where write suggestions of changes to files */ private static CSVWriter suggestions; // /declaratinos // methods /** * guts of Qualified macro expansion * * @param reference the qualified package.class.method name to format. * @param fileBeingProcessed file where macro is embedded * * @return html expansion for the macro * @see com.mindprod.htmlmacros.macro.JDK * @see com.mindprod.htmlmacros.macro.JRE * @see com.mindprod.htmlmacres.macro.JRELocation */ static String decorate( String reference, boolean quoted, final File fileBeingProcessed ) { // we have names such as // J:\Program Files\JetBrains\Intellij 3.1 // E:/mindprod/jgloss/ // E:/mindprod/jgloss/jgloss.html // x.html // there are three clues it is likely a directory. // 1. trailing / \ // 2. FileIsDirectory said so // 3. no dots in name. // modify name to get better odds of finding it on roedy's machine // search for \ instead of / to avoid being confused by final boolean isDir = reference.endsWith( "\\" ) || reference.endsWith( "/" ); final char slash = ST.contains( reference, '\\' ) ? '\\' : '/'; if ( reference.contains( "" ) || reference.contains( "" ) ) { throw new IllegalArgumentException( "macro Path " + reference + " contains unwanted embedded highlighting markup." ); } reference = expandAbbreviations( reference ); final String realname = reference.replace( "user", "roedy" ) .replace( " ", " " ) .replace( "J:", "E:" ) .replace( "X:", "F:" ); File f = new File( realname ); if ( f.exists() ) { // we can definitely discover if it is a file or directory if ( f.isDirectory() && !isDir ) { // it as directory, but is marked as file. err.println( WARNING_PREFIX + " " + reference + " is definitely a directory and should have a trailing \\." ); suggest( reference, reference + slash, fileBeingProcessed ); } else if ( f.isFile() & isDir ) { // it is file, but is marked as directory. err.println( WARNING_PREFIX + " " + reference + " is definitely a file and should not have a trailing \\." ); // convert it to a file without a \ // later they may want to add a . String newname = reference.substring( 0, reference.length() - 1 ); if ( !reference.contains( "." ) ) { newname += "."; } if ( !reference.endsWith( "lib\\ext." ) ) { suggest( reference, newname, fileBeingProcessed ); } } } // / does not count as a separate chunk. It is filtered out String[] chunks = FILE_SPLITTER.split( reference ); FastCat sb = new FastCat( chunks.length * 4 + 7 ); // sandwich the whole thing in to suppress word wrap sb.append( "\n" ); if ( quoted ) { sb.append( """ ); } for ( int i = 0; i < chunks.length; i++ ) { final String chunk = chunks[ i ]; if ( chunk.length() == 0 ) { continue; } else if ( ST.contains( chunk, ':' ) ) { // do the lead directory chunk char driveLetter = chunk.charAt( 0 ); if ( !ST.isLegal( driveLetter, "CJX" ) ) { err.println( WARNING_PREFIX + "drive letter not C: J: X: in " + reference + " in file " + fileBeingProcessed ); } sb.append( "", chunk, "" ); sb.append( slash ); // without cssclass. } else if ( isDir || i < chunks.length - 1 ) { // do an intermediate directory leg. if ( ST.contains( chunk, '.' ) && !Misc.isAnyPossibilitySubstringOfQuery( DIRS_WITH_DOTS_OK, chunk ) ) { err.println( WARNING_PREFIX + "Unexpected dot in directory name in " + reference + " in file " + fileBeingProcessed ); // no suggestion } sb.append( "", chunk, "" ); sb.append( slash ); // without cssclass / or \ normalised // final / will de present on dirs, even if macro did not specify it. } else { // do the final file leg if ( !ST.contains( chunk, '.' ) ) { err.println( WARNING_PREFIX + "missing dot in filename, or missing trailing \\ in " + reference + " in file " + fileBeingProcessed ); // could be either if ( !f.exists() ) { // if file exists, we have already donue the suggestions suggest( reference, reference + slash, fileBeingProcessed ); if ( !reference.endsWith( "lib\\ext" ) ) { suggest( reference, reference + ".", fileBeingProcessed ); } } if ( chunk.equals( ".." ) ) { err.println( WARNING_PREFIX + reference + " is missing a trailing \\ in file " + fileBeingProcessed ); suggest( reference, reference + slash, fileBeingProcessed ); } } String fileClass = "file"; if ( chunk.endsWith( ".exe" ) ) { fileClass = "exe"; } else if ( chunk.endsWith( ".bat" ) || chunk.endsWith( ".btm " ) ) { fileClass = "bat"; } sb.append( "", chunk, "" ); } } // end for if ( quoted ) { sb.append( """ ); } sb.append( "" ); return sb.toString(); }// /method private static String expandAbbreviations( String reference ) { if ( reference.charAt( 0 ) == '%' ) { final int p = reference.indexOf( '\\' ); if ( p >= 0 ) { final String abbr = reference.substring( 1, p ); final String tail = reference.substring( abbr.length() + 1 ); switch ( abbr ) { case "APPDATA": reference = "C:\\Users\\user\\AppData" + tail; break; case "C32": reference = "C:\\Program Files (x86)" + tail; break; case "C64": reference = "C:\\Program Files" + tail; break; case "DS": reference = "C:\\Documents and Settings" + tail; break; case "J32": reference = "J:\\Program Files (x86)" + tail; break; case "J64": reference = "J:\\Program Files" + tail; break; case "JDK32": reference = JDKandJREVersions.JDK_PATH32 + tail; break; case "JDK64": reference = JDKandJREVersions.JDK_PATH + tail; break; // handle %JDK JRE abbreviations case "JRE32": // JRE_PATH32=C:\Program Files (x86)\java\jre1.8.0_131\ reference = JDKandJREVersions.JRE_PATH32 + tail; break; case "JRE64": reference = JDKandJREVersions.JRE_PATH + tail; break; case "PROGRAMDATA": reference = "C:\\ProgramData" + tail; break; case "X32": reference = "X:\\Program Files (x86)" + tail; break; case "X64": reference = "X:\\Program Files" + tail; break; default: throw new IllegalArgumentException( "Unrecognised abbreviation in " + reference ); } // end switch } // end if } // end if return reference; } /** * make a suggested filename echange. * * @param oldname current nam of the path * @param newname suggested new name of the path * @param fileBeingProcessed file wher path macro in embedded */ private static void suggest( final String oldname, final String newname, final File fileBeingProcessed ) { suggestions.put( "" ); suggestions.put( "" ); suggestions.put( fileBeingProcessed.getAbsolutePath() ); suggestions.nl(); }// /method /** * will be called by htmlmacros fireup. */ public static void fireup() { // open a file where we can write suggestions of change to make to files. try { final File wFile = new File( webrootDir, "embellishment/suggestedchanges.csv" ); suggestions = new CSVWriter( EIO.getPrintWriter( wFile, 64 * 1024, EIO.UTF8 ) ); suggestions.nl( "Generated by HTMLMacros Path macro" ); suggestions.nl( "Edit then run CSVReplaceStrings.exe suggestedchanges.csv" ); } catch ( IOException e ) { err.println( "Path: failure to open suggestedchanges.csv" ); System.exit( 2 ); } }// /method public static void shutdown() { if ( suggestions != null ) { // close the file where we can write suggestions of change to make to files.. suggestions.close(); } }// /method /** * Qualified java.lang.Long.parseLong expands to put css classes around each piece on one line * * @param parms parm to keep together. * @param quiet true if want output suppressed. * @param verbose @return expanded macro text */ public String expandMacro( String[] parms, final boolean quiet, final boolean verbose ) { if ( !quiet ) { out.print( "P" ); } if ( parms.length != 1 ) { throw new IllegalArgumentException( USAGE + "\nShould just be path." ); } final String reference = parms[ 0 ].trim(); final boolean quoted = reference.contains( " " ); // if has trailing / on \ it is a dir. return decorate( reference, quoted, fileBeingProcessed ); }// /method // /methods }