// // Copyright (C) 2010 United States Government as represented by the // Administrator of the National Aeronautics and Space Administration // (NASA). All Rights Reserved. // // This software is distributed under the NASA Open Source Agreement // (NOSA), version 1.3. The NOSA has been approved by the Open Source // Initiative. See the file NOSA-1.3-JPF at the top of the distribution // directory tree for the complete NOSA document. // // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. // package gov.nasa.jpf.util; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.io.Reader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * utility class for JPF site configuration related functions. This is partially redundant to Config since * it is used during bootstrapping, and gov.nasa.jpf.Config might not be in the classpath yet. It mostly * differs in terms of key/value expansion, which is only partially supported here. * * NOTE - this class is not allowed to use any JPF specific types */ public class JPFSiteUtils { //--- preparse support - we need this if we use app properties to locat lower level property files static Pattern keyValPattern = Pattern.compile("^[ \t]*([^# \t][^ \t]*)[ \t]*=[ \t]*(.+?)[ \t]*$"); /** * minimal parsing - only local key, system property and and config_path expansion * NOTE this stops after finding the key, and it doesn't add the file to the 'sources' */ public static String getMatchFromFile (String pathName, String lookupKey){ String value = null; Pattern lookupPattern = Pattern.compile(lookupKey); File propFile = new File(pathName); if (!propFile.isFile()){ return null; } HashMap<String, String> map = new HashMap<String, String>(); String dir = propFile.getParent(); if (dir == null) { dir = "."; } map.put("config_path", dir); try { FileReader fr = new FileReader(propFile); BufferedReader br = new BufferedReader(fr); for (String line = br.readLine(); line != null; line = br.readLine()) { Matcher m = keyValPattern.matcher(line); if (m.matches()) { String key = m.group(1); String val = m.group(2); val = expandLocal(val, map); if ((key.length() > 0) && (val.length() > 0)) { // check for continuation lines if (val.charAt(val.length() - 1) == '\\') { val = val.substring(0, val.length() - 1).trim(); for (line = br.readLine(); line != null; line = br.readLine()) { line = line.trim(); int len = line.length(); if ((len > 0) && (line.charAt(len - 1) == '\\')) { line = line.substring(0, line.length() - 1).trim(); val += expandLocal(line, map); } else { val += expandLocal(line, map); break; } } } Matcher lookupMatcher = lookupPattern.matcher(key); if (lookupMatcher.matches()) { value = val; break; } else { if (key.charAt(key.length() - 1) == '+') { key = key.substring(0, key.length() - 1); String v = map.get(key); if (v != null) { val = v + val; } } else if (key.charAt(0) == '+') { key = key.substring(1); String v = map.get(key); if (v != null) { val = val + v; } } map.put(key, val); } } } } br.close(); } catch (FileNotFoundException fnfx) { return null; } catch (IOException iox) { return null; } return value; } /** * this returns the contents of a config source in-order, without expanding values or keys */ public static List<Pair<String,String>> getRawEntries (Reader reader) throws IOException { ArrayList<Pair<String,String>> list = new ArrayList<Pair<String,String>>(); BufferedReader br = new BufferedReader(reader); for (String line = br.readLine(); line != null; line = br.readLine()) { Matcher m = keyValPattern.matcher(line); if (m.matches()) { String key = m.group(1); String val = m.group(2); if ((key.length() > 0) && (val.length() > 0)) { // check for continuation lines if (val.charAt(val.length() - 1) == '\\') { val = val.substring(0, val.length() - 1).trim(); for (line = br.readLine(); line != null; line = br.readLine()) { line = line.trim(); int len = line.length(); if ((len > 0) && (line.charAt(len - 1) == '\\')) { line = line.substring(0, line.length() - 1).trim(); val += line; } else { val += line; break; } } } list.add( new Pair<String,String>(key,val)); } } } return list; } // simple non-recursive, local key and system property expander private static String expandLocal (String s, HashMap<String,String> map) { int i, j = 0; if (s == null || s.length() == 0) { return s; } while ((i = s.indexOf("${", j)) >= 0) { if ((j = s.indexOf('}', i)) > 0) { String k = s.substring(i + 2, j); String v = null; if (map != null){ v = map.get(k); } if (v == null){ v = System.getProperty(k); } if (v != null) { s = s.substring(0, i) + v + s.substring(j + 1, s.length()); j = i + v.length(); } else { s = s.substring(0, i) + s.substring(j + 1, s.length()); j = i; } } } return s; } public static File getCoreDir (File siteProps){ if (siteProps != null){ String path = getMatchFromFile(siteProps.getAbsolutePath(), "jpf-core"); if (path != null) { File coreDir = new File(path); if (coreDir.isDirectory()) { return coreDir; } } } return null; } public static File getSiteCoreDir (String[] args){ File siteProps = getSiteProperties( args); return getCoreDir( siteProps); } /** * get location of jpf-core from site.properties * @return null if it doesn't exist */ public static File getSiteCoreDir() { File siteProps = getStandardSiteProperties(); return getCoreDir( siteProps); } /** * find project properties (jpf.properties) from current dir */ public static File getCurrentProjectProperties() { File d = new File(System.getProperty("user.dir")); do { File f = new File(d, "jpf.properties"); if (f.isFile()){ return f; } d = d.getParentFile(); } while (d != null); return null; } static Pattern idPattern = Pattern.compile("^[ \t]*([^# \t][^ \t]*)[ \t]*=[ \t]*\\$\\{config_path\\}"); static String projectId; /** * look for a "<id> = ${config_path}" entry in current dir/jpf.properties * this looks recursively upwards * @return null if no jpf.properties found */ public static String getCurrentProjectId (){ if (projectId == null) { File propFile = getCurrentProjectProperties(); if (propFile != null) { try { FileReader fr = new FileReader(propFile); BufferedReader br = new BufferedReader(fr); for (String line = br.readLine(); line != null; line = br.readLine()) { Matcher m = idPattern.matcher(line); if (m.matches()) { projectId = m.group(1); } } br.close(); } catch (FileNotFoundException fnfx) { return null; } catch (IOException iox) { return null; } } } return projectId; } public static boolean isFreeArg (String a){ return ((a != null) && (a.length() > 0) && a.charAt(0) != '+' && a.charAt(0) != '-'); } public static File getSiteProperties (String[] args){ //--- 1. check for a +site=<path> argument up to first free arg for (int i=0; i<args.length; i++){ String a = args[i]; if (!isFreeArg(a)){ if (a.startsWith("+site=")) { String path = a.substring(6).trim(); return new File(path); } } else { break; } } //--- 2. check if the first free arg is an application property file (*.jpf), and it contains a 'site=..' setting for (int i=0; i<args.length; i++){ String a = args[i]; if (isFreeArg(a)){ if (a.matches("[^+-].*\\.jpf")) { String path = getMatchFromFile(a, "site"); if (path != null) { return new File(path); } } break; } } //--- 3. finally, check upwards from the current dir up to the home dir return JPFSiteUtils.getStandardSiteProperties(); } /** * locate the site.properties. Start with the current dir, go upwards until the * user.home is reached. If site.properties isn't found there, look for '.jpf' and * 'jpf' dirs within the home dir. If no site.properties is found there either, give up */ public static File getStandardSiteProperties(){ String userDir = System.getProperty("user.dir"); File dir = new File(userDir); for (; dir != null; dir = dir.getParentFile()) { File f = new File(dir, "site.properties"); if (f.isFile()) { return f; } } String[] jpfDirCandidates = { ".jpf", "jpf" }; String userHome = System.getProperty("user.home"); for (String jpfDir : jpfDirCandidates){ dir = new File(userHome, jpfDir); if (dir.isDirectory()) { File f = new File(dir, "site.properties"); if (f.isFile()) { return f; } } } return null; } public static String getGlobalSitePropertiesPath() { String userHome = System.getProperty("user.home"); String globalPath = userHome + File.separator + ".jpf" + File.separator + "site.properties"; return globalPath; } public static List<Pair<String,String>> getRawEntries (File siteProps){ FileReader fr = null; if (siteProps.isFile()) { try { fr = new FileReader(siteProps); List<Pair<String,String>> entries = getRawEntries(fr); fr.close(); return entries; } catch (IOException iox) { } finally { try { fr.close(); } catch (IOException _ignore){} } } return new ArrayList<Pair<String,String>>(); } /** * this returns a list of all the project ids in the 'extensions' entries (also * handles accumulated 'extensions+=.." entries */ public static List<String> getExtensions (List<Pair<String,String>> entries){ ArrayList<String> list = new ArrayList<String>(); for (Pair<String,String> p : entries){ if (p._1.startsWith("extensions")){ for (String pid : p._2.split("[,;]")){ pid = pid.trim(); if (pid.charAt(0) == '$'){ pid = pid.substring(2, pid.length()-1); list.add( pid); } } } } return list; } public static boolean addProject (File siteProps, String projectId, File projectDir, boolean isExt){ List<Pair<String,String>> entries = getRawEntries(siteProps); List<String> extensions = getExtensions(entries); if ("jpf-core".equals(projectId)){ // jpf-core always has to be in the extensions list isExt = true; } try { FileUtils.ensureDirs(siteProps); String projectPath = FileUtils.asCanonicalUserPathName(projectDir.getAbsolutePath()); PrintWriter pw = new PrintWriter(siteProps); pw.println("# auto-generated JPF site properties"); pw.println(); boolean alreadyThere = false; for (Pair<String, String> e : entries) { if (!"extensions".equals(e._1)) { pw.print(e._1); pw.print(" = "); if (projectId.equals(e._1)) { alreadyThere = true; // Hmm, not sure its best to use absolute pathnames here (e.g. when doing local site installs) pw.println(projectPath); // check if we have to update extensions if (extensions.contains(projectId)){ if (!isExt){ extensions.remove(projectId); } } else { extensions.add(projectId); } } else { pw.println(e._2); } } } if (!alreadyThere) { if (isExt) { extensions.add(projectId); } pw.print(projectId); pw.print(" = "); pw.println(projectPath); } pw.println(); pw.print("extensions = "); boolean isFirst = true; for (String e : extensions) { if (isFirst) { isFirst = false; } else { pw.print(','); } pw.print("${"); pw.print(e); pw.print('}'); } pw.println(); pw.close(); } catch (IOException iox) { iox.printStackTrace(); return false; } return true; } }