/* ToXml.groovy * Sample script for generating raw XML from the syntax tree and symbol tables. * * --- USAGE --- * * 0. NOTE that ProRefactor writes potentially large amounts of data, * and that this script may use large amounts of CPU, memory, and disc space. * * 1. You must have ProRefactor and Groovy scripting set up. See: * http://www.oehive.org/prorefactor/standalone_jar_file * * 2. Check this script to see if you need to customize anything, for example, * if you use compile unit file names with non-standard extensions * (anything other than *.p, *.w, or *.cls). * See the ONLY_THIS_DIR variable - maybe try one small directory to start. * * 3. Run the script: groovy ToXml.groovy * * ------------- * * Copyright (C) 2008 by Joanju Software (www.joanju.com) * Released under the Eclipse Public License * http://www.eclipse.org/legal/epl-v10.html */ import com.thoughtworks.xstream.XStream import com.thoughtworks.xstream.converters.Converter import com.thoughtworks.xstream.converters.MarshallingContext import com.thoughtworks.xstream.converters.UnmarshallingContext import com.thoughtworks.xstream.io.HierarchicalStreamReader import com.thoughtworks.xstream.io.HierarchicalStreamWriter import org.prorefactor.core.JPNode import org.prorefactor.core.schema.Schema import org.prorefactor.refactor.RefactorSession import org.prorefactor.treeparser.ParseUnit import org.prorefactor.refactor.PUB import java.util.zip.ZipOutputStream import java.util.zip.ZipEntry class ToXml implements Runnable { // CUSTOMIZE ME // Often we only want files from one directory. // Examples: 'c:\\work\\only-me\\', or '/volume2/mycode/' (unix), or '' for all. final String ONLY_THIS_DIR = '' final String OUTDIR = 'prorefactor/xml/' def canonicalPropath = '' def prsession = RefactorSession.instance void run() { if (!loadProject()) return canonicalPropath = canonicalPropath(prsession.progressSettings.propath) // See http://ant.apache.org/manual/CoreTypes/fileset.html // See http://groovy.codehaus.org/Using+Ant+from+Groovy def fileScanner = new AntBuilder().fileScanner { for (String dirname in canonicalPropath) { if (! dirname.startsWith(ONLY_THIS_DIR)) continue fileset(dir:dirname) { // Customize these if you use non-standard extensions. include(name:'**/*.p') include(name:'**/*.w') include(name:'**/*.cls') } } } for (File file in fileScanner) { def relativePath = relativePath(file) println relativePath def pu = new ParseUnit(file) // Load the syntax tree and build the symbol tables. pu.loadOrBuildPUB() // Force the macro graph to be built. pu.getMacroGraph() def xstream = new XStream() xstream.alias('JPNode', JPNode.class) xstream.setMode(XStream.ID_REFERENCES) xstream.registerConverter(new SkipConverter()) def outFile = new File(OUTDIR + relativePath + '.xml.zip') outFile.parentFile.mkdirs() def zipStream = new ZipOutputStream(new FileOutputStream(outFile)) zipStream.putNextEntry(new ZipEntry(file.name + '.xml')) xstream.toXML(pu, zipStream) zipStream.close() } } /* Build a canonical subset of the propath. * This is a sorted list of canonical directory names, * where no entries are subdirectories of any other entries. * This is used for finding compile unit source files, without * having to worry about getting duplicate entries due to nested * propath entries. * Each entry will end with the file path separator char (ex: '/'). */ ArrayList canonicalPropath(String propath) { def set = new TreeSet(); def dropList = new ArrayList(); for (path in propath.split(',')) { // Comparing directory names with startsWith wouldn't // match correctly unless we use trailing slashes. // Consider comparing '/abc' and '/ab', vs // comparing '/abc/' and '/ab/'. def name = new File(path).canonicalPath if (! name.endsWith(File.separator)) name += File.separator set.add(name) } for (path1 in set) { for (path2 in set) { if (path1.equals(path2)) continue if (path1.startsWith(path2)) dropList.add(path1) } } for (drop in dropList) { set.remove(drop) } def ret = new ArrayList(); ret.addAll(set) return ret } /* ProRefactor has to know which project settings to work with. * If there's only one project settings directory, we go with it. * (It's possible to parse without a project, but useless for this script.) */ def loadProject() { final String PROJECTS_DIR = './prorefactor/projects' def projectNames = [] def projectSettingsDir = new File(PROJECTS_DIR) if (projectSettingsDir.exists()) projectSettingsDir.eachFile{projectNames << it.name} if (projectNames.size==0) { println "No ProRefactor project settings found in $PROJECTS_DIR" return false } String projectName = null if (projectNames.size==1) projectName = projectNames[0] while (projectName==null) { print "Enter the project name used for ProRefactor settings: " projectName = System.in.readLine() if (! projectNames.contains(projectName)) { println "'$projectName' was not found in $PROJECTS_DIR" projectName = null } } try { println "Loading $projectName settings" prsession.loadProject(projectName) } catch(Exception e) { println 'Failed to load project.' println e return false } return true } String relativePath(File file) { for (String s in canonicalPropath) { if (file.path.startsWith(s)) return file.path.substring(s.length()) } return file.path } } /** Converter for 'skipped' objects. * We don't need to clutter up the XML with a full dump of these for every compile unit. */ class SkipConverter implements Converter { public boolean canConvert(Class aClass) { switch (aClass) { case Schema: case PUB: return true default: return false } } public void marshal(Object o, HierarchicalStreamWriter writer, MarshallingContext marshallingContext) { switch (o) { case Schema: writer.startNode('schema') writer.endNode() break case PUB: writer.startNode('pub') writer.endNode() break } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext unmarshallingContext) { return null; } }