Simple Parser Example
If you'd like to use StrataCode's code-processing APIs for Java, or another support language, use Parselets to define your own language, without defining layers, or using any of the frameworks, this is a good place to start. Look in bundles/example/example/simpleParser:
Readme.md:

Simple Parser

This example shows you how to use apis to manipulate Java code without specifying layer definition files and using the normal "LayeredSystem" main routine which is used by the 'scc' command.

Instead, you create the LayeredSystem programmatically, configure it's options, then use methods on the system to retrieve JavaModel or TypeDeclaration instances. These are the modifiable AST objects.

This example reads one or more types, enumerates the fields, and methods in each top-level class, and optionally does a simple replace of a class name, method name, or field name. Although we do not show changes expressions using those types, that's feasible by digging just a little deeper into the API.

You run the program with a srcPath containing Java source files. You can also set an optional classPath to support any source in that srcPath.

Running the example

Edit run.sh script, change the path to where you have built or installed the "sc.jar".

See runTest.sh for a set of tests on the testSrc directory provided.

If you use -find , you provide a prefix for matching class names. In this case we load all source files and create a type index to do the search more efficiently. It's stored in the 'idx' directory where you run the sample.

SimpleParser.java:
package sc.simpleParser;

import sc.lang.ILanguageModel;
import sc.lang.java.*;
import sc.layer.*;
import sc.parser.ParseUtil;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.TreeSet;

/** 
 * This is a simple class which you can use a sample to learn how to create a parser from
 * a srcPath and classPath.  It has a few options but basically works to either print or do a find/replace
 * operation on one type or a set of matching types.  It does not yet edit expressions but it
 * could be enhanced to be a real find-replace tool.
 *
 * Although this uses StrataCode, for your example you'll have only one 'layer' called 'sysLayer'.  It's an
 * inactive layer because this tool is not going to run any code, just edit source more like an IDE.
 *
 * You specify class files in a traditional class path but you have a choice for how those classes are loaded.
 * Either the regular classPath, this processes class loader is used to load .class files and use to resolve
 * references.  For externalClassPath, these classes are loaded using the built-in StrataCode CFClass library which
 * reads .class files just for their types, fields, method declarations, etc.  
 * You can use both at the same time, but it's not defined when there are references from classPath classes to externalClassPath classes.
 * The reverse direction is fine.
 * Using classPath can avoid loading classes twice but runs into some limitations so externalClassPath is probably the best way to go
 * for most purposes.
 */
public class SimpleParser {
   LayeredSystem sys; // Think of this as your 'project' in StrataCode.  It's used for everything including a set of inactive and inactive layers - inactive are used for editing, active for build/run
   Layer sysLayer; // Here we just create one layer to hold all of the info specified in the classPath/srcPath.

   String classPath = null, externalClassPath = null, srcPath = null;

   String typeName = null, findName = null;

   String fromName = null, toName = null;

   boolean doFields = true;
   boolean doMethods = true;
   boolean doTypes = true;

   boolean outputOnly = true;

   public static void main(String[] args) {
      SimpleParser sp = new SimpleParser(args);
      sp.run();
   }

   public SimpleParser(String[] args) {
      initOptions(args);

      // The first argument is is the 'main-directory' - i.e. where we store the typeIndex
      sys = ParseUtil.createSimpleParser(".", classPath, externalClassPath, srcPath, null);
      // In case we use initTypeIndex, this will maintain this incrementally for speed.  Use TypeIndexModel.Rebuild to just rebuild it each time.
      sys.options.typeIndexMode = TypeIndexMode.Refresh;

      // The simple parser creates a single layer to represent the classPath/srcPath you provided.  Since we are not running this code, it creates
      // an inactive layer.  We need this layer for some of the APIs which work either in active or inactive mode like getSrcTypeDeclaration().
      sysLayer = sys.inactiveLayers.get(0);
   }

   /** Iterate over the types selected from the command line and either print or do a find-replace and print the resulting code */
   public void run() {
      ArrayList<TypeDeclaration> types = new ArrayList<TypeDeclaration>();
      if (typeName != null) {
         TypeDeclaration type = sys.getSrcTypeDeclaration(typeName, sysLayer);
         if (type == null)
            System.err.println("*** typeName not found: " + typeName);
         else
            types.add(type);
      }
      if (findName != null) {
         // This will be expensive if there are lots of files in the srcPath - it will parse them the first time and refresh the index each time for changes
         sys.initTypeIndex();

         LinkedHashSet<String> matchingTypeNames = new LinkedHashSet<String>();
         // NOTE: findName here is a prefix, not a regex or anything.  We should make that more flexible!
         sys.findMatchingGlobalNames(null, sysLayer, findName, matchingTypeNames, true, false);
         if (matchingTypeNames.size() == 0)
            System.out.println("*** No matches with prefix: " + findName);
         else {
            System.out.println("Found: " + matchingTypeNames);
            for (String matchingTypeName:matchingTypeNames) {
               TypeDeclaration type = sys.getSrcTypeDeclaration(matchingTypeName, sysLayer);
               if (type == null)
                  System.err.println("*** No src found for: " + matchingTypeName);
               else
                  types.add(type);
            }
         }
      }
      for (TypeDeclaration type:types) {
         JavaModel model = type.getJavaModel();
         boolean modelChanged = false;
         if (doTypes) {
            if (toName == null) {
               System.out.println(type.toDeclarationString());
            }
            else if (type.typeName.equals(fromName)){
               System.out.println("*** Before: " + type.toDeclarationString());
               type.updateTypeName(toName, !outputOnly && type.getEnclosingType() == null);
               System.out.println("*** After: " + type.toDeclarationString());
               modelChanged = true;
            }
         }
         if ((doFields || doMethods) && type.body != null) {
            for (Statement st:type.body) {
               if (doFields && st instanceof FieldDefinition) {
                  if (toName == null) {
                     System.out.println(st.toDeclarationString());
                  }
                  else {
                     FieldDefinition field = (FieldDefinition) st;
                     for (VariableDefinition varDef:field.variableDefinitions) {
                        if (varDef.variableName.equals(fromName)) {
                           System.out.println("*** Before: " + field.toDeclarationString());
                           varDef.setProperty("variableName", toName);
                           System.out.println("*** After: " + field.toDeclarationString());
                           modelChanged = true;
                        }
                     }
                  }
               }
               else if (doMethods && st instanceof AbstractMethodDefinition) {
                  if (toName == null) {
                     System.out.println(st.toDeclarationString());
                  }
                  else {
                     AbstractMethodDefinition meth = (AbstractMethodDefinition) st;
                     if (meth.name != null && meth.name.equals(fromName)) {
                        System.out.println("*** Before: " + meth.toDeclarationString());
                        meth.setProperty("name", toName);
                        System.out.println("*** After: " + meth.toDeclarationString());
                        modelChanged = true;
                     }
                  }
               }
            }
         }
         // Call this after making any setProperty calls anywhere in the
         // AST.  By default, the model does not track changes on every
         // API call but you can also change that.  It can be more 
         // efficient to validate the saved model afterwards if you make
         // lots of changes to the statements.
         if (outputOnly) {
            if (modelChanged)
               model.validateSavedModel(true);
            System.out.println(model.toLanguageString());
         }
         else if (modelChanged) {
            System.out.println("*** Updating file: " + model.getSrcFile());
            model.saveModel();
         }
      }
   }

   public void initOptions(String[] args) {
      for (int i = 0; i < args.length; i++) {
         String arg = args[i];
         if (arg.equals("-apply"))
            outputOnly = false;
         else if (arg.equals("-t")) {
            doFields = false;
            doMethods = false;
            doTypes = true;
         }
         else if (arg.equals("-f")) {
            doFields = true;
            doMethods = false;
            doTypes = false;
         }
         else if (arg.equals("-m")) {
            doFields = false;
            doMethods = true;
            doTypes = false;
         }
         else if (i < args.length - 1) {
            if (arg.equals("-cp"))
               classPath = args[++i];
            else if (arg.equals("-ext"))
               externalClassPath = args[++i];
            else if (arg.equals("-srcPath"))
               srcPath = args[++i];
            else if (arg.equals("-typeName"))
               typeName = args[++i];
            else if (arg.equals("-find"))
               findName = args[++i];
            else if (arg.equals("-from"))
               fromName = args[++i];
            else if (arg.equals("-to"))
               toName = args[++i];
            else
               System.err.println("*** Unrecognized arg: " + arg);
         }
         else
            System.err.println("*** Unrecognized arg: " + arg);
      }
   }
}