package cn.bran.japid.util; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.text.CharacterIterator; import java.text.StringCharacterIterator; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import cn.bran.japid.classmeta.AbstractTemplateClassMetaData; import cn.bran.japid.template.JapidRenderer; public class DirUtil { public static final String OF = ", "; public static final String LINE_MARKER = "// line "; static final String DOT_SUB = "_d_"; private static final String[] ALL_EXTS = new String[] { ".java", ".html", ".js", ".txt", ".css", ".json", ".xml" }; private static final String[] TEMPLATE_EXTS = new String[] { ".html", ".js", ".txt", ".css", ".xml", ".json" }; public static Set<File> findOrphanJava(File src, File target) { if (target == null) target = src; String[] allSrc = getAllFileNames(src, ALL_EXTS); Set<String> javas = new HashSet<String>(); Set<String> srcFiles = new HashSet<String>(); for (String s : allSrc) { if (s.endsWith(".java")) { javas.add(s); } else /* if (s.endsWith(".html")) */{ srcFiles.add(mapSrcToJava(s)); } } javas.removeAll(srcFiles); Set<File> re = new HashSet<File>(); for (String j : javas) { re.add(new File(j)); } return re; } /** * */ public static String[] getAllFileNames(File dir, String[] exts) { List<String> files = new ArrayList<String>(); getAllFileNames("", dir, files, exts); String[] ret = new String[files.size()]; return files.toArray(ret); } public static String[] getAllTemplateHtmlFiles(File dir) { List<String> files = new ArrayList<String>(); getAllFileNames("", dir, files, new String[] { ".html" }); // should filter out bad named files String[] ret = new String[files.size()]; return files.toArray(ret); } public static String[] getAllTemplateFileNames(File dir) { List<String> files = new ArrayList<String>(); getAllFileNames("", dir, files, TEMPLATE_EXTS); // should filter out bad named files String[] ret = new String[files.size()]; return files.toArray(ret); } public static String[] getAllTemplateFileNames(String... dirs) { List<String> re = new ArrayList<String>(); for (String dir : dirs) { File dirF = new File(dir); if (dirF.exists()) { List<String> files = new ArrayList<String>(); getAllFileNames("", dirF, files, TEMPLATE_EXTS); re.addAll(files); } } // should filter out bad named files String[] ret = new String[re.size()]; return re.toArray(ret); } /** * collect all files with one of the extensions from the directory. * * @param dir * @param exts * @param fs * @return the files match. Note the files path starts with the source dir. */ public static Set<File> getAllFiles(File dir, String[] exts, Set<File> fs) { Set<File> scanFiles = scanFiles(dir, exts, fs); return scanFiles; } /** * @param dir * @param exts * @param fs * @return */ private static Set<File> scanFiles(File dir, String[] exts, Set<File> fs) { File[] flist = dir.listFiles(); for (File f : flist) { if (f.isDirectory()) getAllFiles(f, exts, fs); else { if (match(f, exts)) fs.add(f); } } return fs; } /** * retrieve all the files with specified extensions recursively in a * directory and its sub-directory * * @author Bing Ran (bing.ran@hotmail.com) * @param dir * @param exts * @return */ public static Set<File> scanFiles(File dir, String[] exts) { Set<File> files = new HashSet<File>(); for (File f : dir.listFiles()) { if (f.isDirectory()) files.addAll(scanFiles(f, exts)); else { if (match(f, exts)) files.add(f); } } return files; } public static Set<File> getAllTemplateFiles(File root) { File dir = new File(root, "japidviews"); // is this requirement really // necessary? if (!dir.exists()) throw new RuntimeException(dir.getPath() + " does not exist."); return scanFiles(dir, TEMPLATE_EXTS); } private static void getAllFileNames(String leadingPath, File dir, List<String> files, String[] exts) { if (!dir.exists()) return; // throw new RuntimeException("directory exists? " + dir.getPath()); File[] flist = dir.listFiles(); for (File f : flist) { if (f.isDirectory()) getAllFileNames(leadingPath + f.getName() + File.separatorChar, f, files, exts); else { if (match(f, exts)) files.add(leadingPath + f.getName()); } } } static boolean match(File f, String[] exts) { for (String ext : exts) { if (f.getName().endsWith(ext)) if (fileNameIsValidClassName(f)) return true; } return false; } // private static boolean firstTimeDetectingChanges = true; // private static final ThreadLocal<Set<File>> versionCheckedDirs = new ThreadLocal<Set<File>>() { // @Override protected Set<File> initialValue() { // return new HashSet<File>(); // } // }; public static String curVersion; public static List<File> findChangedSrcFiles(File srcDir) { // if (target == null) // target = src; // String srcPath = src.getPath(); Set<File> allSrc = new HashSet<File>(); allSrc = getAllFiles(srcDir, ALL_EXTS, allSrc); // <name, last modified time> Map<String, Long> javaFiles = new HashMap<String, Long>(); Map<String, Long> scriptFiles = new HashMap<String, Long>(); long now = System.currentTimeMillis(); boolean hasFutureTimestamp = false; for (File f : allSrc) { String path = f.getPath(); long modi = f.lastModified(); // System.out.println("file: " + path + ":" + modi); if (path.endsWith(".java")) { if (modi > now) { // for some reason(e.g., copies from other file system), the last modified time is in the future // let's reset it. hasFutureTimestamp = true; f.setLastModified(now - 1); } javaFiles.put(path, modi); } else { // validate file name to filter out dubious files such as // temporary files if (fileNameIsValidClassName(f)) { if (modi > now) { // for some reason(e.g., copies from other file system), the last modified time is in the future // let's reset it. hasFutureTimestamp = true; f.setLastModified(now); } scriptFiles.put(path, modi); } } } if (hasFutureTimestamp) { JapidFlags.warn("Some of the Japid files have a timestamp in the future. It could have been caused by out-of-synch system time."); } List<File> changedScripts = new ArrayList<File>(); boolean japidVersionChecked = JapidFlags.isDirVersionChecked(srcDir); if (!japidVersionChecked) JapidFlags.debug("to version-check the japid scripts in: " + srcDir + ". Current version is: " + curVersion); for (String script : scriptFiles.keySet()) { String javaFileName = mapSrcToJava(script); File javaFile = new File(javaFileName); Long javaTimestamp = javaFiles.get(javaFileName); if (javaTimestamp == null) { // how can this happen? // javaFile.delete(); changedScripts.add(new File(script)); } else { Long srcStamp = scriptFiles.get(script); if (srcStamp.compareTo(javaTimestamp) > 0) { // javaFile.delete(); changedScripts.add(new File(script)); } else { // if (firstTimeDetectingChanges) { if (!japidVersionChecked) { // check the japid version that the java code was // generated by if (javaFile.exists()) { String firstLine = JapidRenderer.readFirstLine(javaFile); if (firstLine.startsWith(AbstractTemplateClassMetaData.VERSION_HEADER)) { firstLine = firstLine.substring(AbstractTemplateClassMetaData.VERSION_HEADER.length()) .trim(); if (!curVersion.equals(firstLine)) { JapidFlags.debug("japid version mismatch. to refresh " + javaFileName); // javaFile.delete(); changedScripts.add(new File(script)); } else { JapidFlags.debug("japid version matches for " + javaFileName); } } else { JapidFlags.debug("japid version mismatch. to refresh " + javaFileName); // javaFile.delete(); changedScripts.add(new File(script)); } } } } } } // versionCheckedDirs.get().add(srcDir); JapidFlags.dirVersionChecked(srcDir); // firstTimeDetectingChanges = false; return changedScripts; } /** * @author Bing Ran (bing.ran@hotmail.com) * @param f * @return */ private static boolean fileNameIsValidClassName(File f) { String fname = f.getName(); if (fname.startsWith(".")) return false; fname = fname.substring(0, fname.lastIndexOf(".")).replace('.', '_'); return isClassname(fname); } /** * map template source file name to the generated java file name * * @param k * @return */ public static String mapSrcToJava(String k) { if (k.endsWith(".java")) return k; if (k.endsWith(".txt")) { return getRoot(k) + "_txt" + ".java"; } else if (k.endsWith(".xml")) { return getRoot(k) + "_xml" + ".java"; } else if (k.endsWith(".json")) { return getRoot(k) + "_json" + ".java"; } else if (k.endsWith(".css")) { return getRoot(k) + "_css" + ".java"; } else if (k.endsWith(".js")) { return getRoot(k) + "_js" + ".java"; } else { // including html return getRoot(k) + ".java"; } } /** * * map java source file name to the template file name * * @param k * @return */ public static String mapJavaToSrc(String k) { return rawConvert(k);// .replaceAll(DOT_SUB, "."); } private static String rawConvert(String k) { if (k.endsWith(".java")) k = k.substring(0, k.lastIndexOf(".java")); if (k.endsWith("_txt")) { return k.substring(0, k.lastIndexOf("_txt")) + ".txt"; } else if (k.endsWith("_xml")) { return k.substring(0, k.lastIndexOf("_xml")) + ".xml"; } else if (k.endsWith("_json")) { return k.substring(0, k.lastIndexOf("_json")) + ".json"; } else if (k.endsWith("_css")) { return k.substring(0, k.lastIndexOf("_css")) + ".css"; } else if (k.endsWith("_js")) { return k.substring(0, k.lastIndexOf("_js")) + ".js"; } else { // including html return k + ".html"; } } private static String getRoot(String k) { for (String ext : TEMPLATE_EXTS) { if (k.endsWith(ext)) { String sub = k.substring(0, k.lastIndexOf(ext)); // String first = ""; // if (sub.startsWith(".")) { // keep the first dot, which means // current dir // first = "."; // sub = sub.substring(1); // } // return first + sub.replaceAll("\\.", DOT_SUB); return sub; } } return k; } public static void writeStringToFile(File file, String content) throws IOException { Writer fw = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); BufferedWriter bw = new BufferedWriter(fw); bw.append(content); bw.close(); } public static void touch(File newer) throws IOException { writeStringToFile(newer, ""); } public static boolean containsTemplateFiles(String root, String dirName) { String sep = File.separator; String japidViews = root + sep + JAPIDVIEWS_ROOT + sep; String dir = japidViews + dirName; return containsTemplatesInDir(dir); } public static boolean containsTemplatesInDir(String dirName) { File dir = new File(dirName); if (dir.exists()) { String[] temps = getAllFileNames(dir, ALL_EXTS); if (temps.length > 0) return true; else return false; } else return false; } // public static boolean hasTags(String root) { // String dirName = DirUtil.TAGSDIR; // return containsTemplateFiles(root, dirName); // } // public static boolean hasJavaTags(String root) { // String dirName = DirUtil.JAVATAGS; // return containsTemplateFiles(root, dirName); // } // // public static boolean hasLayouts(String root) { // String dirName = DirUtil.LAYOUTDIR; // return containsTemplateFiles(root, dirName); // } // public static boolean isClassname(String classname) { if (classname == null || classname.length() == 0) return false; CharacterIterator iter = new StringCharacterIterator(classname); // Check first character (there should at least be one character for // each part) ... char c = iter.first(); if (c == CharacterIterator.DONE) return false; if (!Character.isJavaIdentifierStart(c) && !Character.isIdentifierIgnorable(c)) return false; c = iter.next(); // Check the remaining characters, if there are any ... while (c != CharacterIterator.DONE) { if (!Character.isJavaIdentifierPart(c) && !Character.isIdentifierIgnorable(c)) return false; c = iter.next(); } return true; } // public static final String JAVATAGS = "_javatags"; // public static final String LAYOUTDIR = "_layouts"; // public static final String TAGSDIR = "_tags"; public static final String JAPIDVIEWS_ROOT = "japidviews"; // public static List<String> scanJavaTags(String root) { // String sep = File.separator; // String japidViews = root + sep + JAPIDVIEWS_ROOT + sep; // File javatags = new File(japidViews + JAVATAGS); // if (!javatags.exists()) { // boolean mkdirs = javatags.mkdirs(); // assert mkdirs == true; // JapidFlags.log("created: " + japidViews + JAVATAGS); // } // // File[] javafiles = javatags.listFiles(new FilenameFilter() { // @Override // public boolean accept(File dir, String name) { // if (name.endsWith(".java")) // return true; // return false; // } // }); // // List<String> files = new ArrayList<String>(); // for (File f : javafiles) { // String fname = f.getName(); // files.add(JAPIDVIEWS_ROOT + "." + JAVATAGS + "." + fname.substring(0, // fname.lastIndexOf(".java"))); // } // return files; // } /** * @author Bing Ran (bing.ran@hotmail.com) * @param f * the java file * @return the original template file */ public static File mapJavatoSrc(File f) { File parent = f.getParentFile(); String fname = mapJavaToSrc(f.getName()); return new File(parent, fname); } /** * * @author Bing Ran (bing.ran@hotmail.com) * @param sourceCode * @param lineNum * 1-based line number in Java file * @return 1-based line number in the original source template */ public static int mapJavaLineToSrcLine(String sourceCode, int lineNum) { String[] codeLines = sourceCode.split("\n"); String line = codeLines[lineNum - 1]; int lineMarker = line.lastIndexOf(LINE_MARKER); if (lineMarker < 1) { return -1; } String linenum = line.substring(lineMarker + LINE_MARKER.length()).trim(); int indexOf = linenum.indexOf(OF); if (indexOf > 0) { linenum = linenum.substring(0, indexOf); } return Integer.parseInt(linenum); } /** * */ static final String ERRORS = "_errors"; /** * */ static final String NOTIFIERS = "_notifiers"; /** * a utility method. * * @param srcDir * @param cf * @throws IOException */ public static String getRelativePath(File child, File parent) throws IOException { String curPath = parent.getCanonicalPath(); String childPath = child.getCanonicalPath(); assert (childPath.startsWith(curPath)); String srcRelative = childPath.substring(curPath.length()); if (srcRelative.startsWith(File.separator)) { srcRelative = srcRelative.substring(File.separator.length()); } return srcRelative; } public static String readFileAsString(String filePath) throws Exception { return readFileAsString(new File(filePath)); } public static String readFileAsString(File file) throws FileNotFoundException, IOException, UnsupportedEncodingException { byte[] buffer = new byte[(int) file.length()]; BufferedInputStream f = new BufferedInputStream(new FileInputStream(file)); f.read(buffer); f.close(); return new String(buffer, "UTF-8"); } public static void copyStream(InputStream in, OutputStream out) throws IOException { byte[] b = new byte[4096]; int read; while ((read = in.read(b)) != -1) { out.write(b, 0, read); } } public static void copyStreamClose(InputStream in, OutputStream out) throws IOException { copyStream(in, out); in.close(); out.close(); } /** * a/b.html -> a.b a/b.xml -> a.b_xml * * @author Bing Ran (bing.ran@hotmail.com) * @param japidScriptFileName * @return */ public static String deriveClassName(String japidScriptFileName) { // remove the leading slash if (japidScriptFileName.startsWith("/") || japidScriptFileName.startsWith("\\")) japidScriptFileName = japidScriptFileName.substring(1); if (japidScriptFileName.startsWith(".")) japidScriptFileName = japidScriptFileName.substring(1); japidScriptFileName = mapSrcToJava(japidScriptFileName); japidScriptFileName = japidScriptFileName.replace('/', '.').replace('\\', '.'); if (japidScriptFileName.endsWith(".java")) { japidScriptFileName = japidScriptFileName.substring(0, japidScriptFileName.length() - 5); } return japidScriptFileName; } /** * @author Bing Ran (bing.ran@hotmail.com) * @param templateRoots * @return */ public static Set<File> getAllTemplateFiles(String[] templateRoots) { Set<File> re = new HashSet<File>(); for (String r : templateRoots) { File root = new File(r); if (root.exists()) re.addAll(getAllTemplateFiles(root)); } return re; } /** * get all the java files in a dir with the "java" removed * * @return */ public static String[] getAllJavaFilesInDir(File root) { // from source files only String[] allFiles = getAllFileNames(root, new String[] { ".java" }); for (int i=0; i< allFiles.length; i++) { allFiles[i] = allFiles[i].replace(".java", ""); } return allFiles; } public static File writeToFile(String jsrc, String realTargetFile) throws FileNotFoundException, IOException, UnsupportedEncodingException { File f = new File(realTargetFile); if (!f.exists()) { String parent = f.getParent(); new File(parent).mkdirs(); } BufferedOutputStream bf = new BufferedOutputStream(new FileOutputStream(f)); bf.write(jsrc.getBytes("UTF-8")); bf.close(); return f; } }