Standalone Jar File

ProRefactor can be used as a standalone library, entirely independently of Eclipse.

This Java archive (jar) file contains ProRefactor, as well as the third party libraries it requires.

prorefactor.oehive.org/prorefactor.jar
Last updated: June 17, 2008. About a dozen MB.

Setting Up ProRefactor

  1. See Project Config. Dump for a script which creates a prorefactor project settings directory.
  2. Proparse must be downloaded separately: joanju.com/proparse/. The Proparse shared library (.dll or .so) must be in your library path. On Windows, this can simply be your working directory. On most unixes, you would set an environment variable. For example, on Linux you might use:

    export LD_LIBRARY_PATH=.

    ProRefactor.jar always requires the latest release of Proparse.

  3. Remember that ProRefactor may write very large amounts of data to into the 'prorefactor' sub-directory of your working directory. If your application is very large, then this could be as much as a few GB. See PUB Files

Setting Up Scripting

This section assumes you will be using Groovy for scripting, but other JVM scripting languages will be similar.

  1. Install the latest version of Groovy (assuming you have Java installed): groovy.codehaus.org
  2. Open a command line prompt. Change to your prorefactor working directory. Test that groovy runs OK with: groovy --version
  3. Make sure your CLASSPATH environment variable contains prorefactor.jar. For example, on Linux you might use:

    export CLASSPATH=prorefactor.jar

    assuming prorefactor.jar is in the working directory. (If you need additional jar files, don't forget on unix, use ':' instead of ';' to separate classpath entries.)


Console

Using the standalone jar file, you can launch a command line console which contains one or two examples of how you can use the ProRefactor libraries. The console (org.prorefactor.Console) is the default class for the standalone jar file, so you can launch it simply with:

java -jar prorefactor.jar

Here's what its menu looks like as of Jan 2007:

h) How-used report
l) Load Settings for a project
p) Parse a directory
q) Quit
v) show proparse Version
w) Where used report

Enter selection:

Showing the Proparse version is useful for checking that the Proparse shared library can be found and loaded OK. The parse a directory option is useful for checking that your ProRefactor project settings are correct, and that ProRefactor can parse your application OK.


PUB Files

In prorefactor/projects/projectname/pubs in your working directory, ProRefactor writes out a parse unit binary (PUB) file for each compile unit in your application. These will be built as needed, when you do a parse or run a report.

These PUB files contain the full syntax tree and some additional information, so that your programs do not need to be re-parsed each time ProRefactor wants to get information about them. The *.pub files can be thought of a bit like your *.r files.

Here's the important bit: These files contain a fair amount of information, and will probably require more disc space than your .r files. In fact, for a very large enterprise application, the PUB files might require as much as a few gigabytes of disc space.


Where Used Report

This is not the most efficient way to find where a database field is used, but it is a useful example of using ProRefactor.

See this WebSVN link for the full source.

It prompts for a fully qualified db.table.field name, checks that it exists in ProRefactor's configured schema, and splits it into two string variables: fieldName, and dbTableName.

It then finds all parse units in the directory of your choice, and loads or builds the PUB file for each of those, and then checks to see if your field is used in that compile unit. So, for each parse unit pu:

PUB pub = pu.getPUB();
boolean wasCurrent = pub.loadTo(PUB.SCHEMA);
if (wasCurrent==false) pub.build();
if (usesField(pub) == false) return;
reportOut.write(pu.getFile().toString());

Next is the relevant code for usesField(). The PUB object has a record of all tables and fields used in that compile unit. We get a copy of the list of fields of the table that were used in the PUB, then check if it contains the name of the field we are interested in.

pub.copySchemaFieldLowercaseNamesInto(fieldNames, dbTableName);
return fieldNames.contains(fieldName);

How Used Report

This report shows a snippet from each line of code where a database field is used. For example, a how-used report for sports2000.customer.custnum might show:

s2k\gui\bcust.w
define temp-table ...  like Customer.CustNum validate

s2k\gui\dcust.w
define temp-table ...  like Customer.CustNum validate

s2k\gui\dcust_cl.w
define temp-table ...  like Customer.CustNum validate

s2k\gui\orderreport.p
format ...  Customer.CustNum column-label "Cust-Num" format ">>>>9"
display ...  Customer.CustNum

s2k\gui\vcust.w
define temp-table ...  like Customer.CustNum validate

s2k\gui\vcust2.w
define temp-table ...  like Customer.CustNum validate

s2k\gui\vordlne2.w
find ...  customer.custnum eq order.custnum
find ...  customer.custnum eq order.custnum

s2k\gui\worder.w
define variable ...  like customer.custnum

See this WebSVN link for the full source.

This report takes a ProRefactor Field object (reportField) as a parameter, and then walks the syntax tree for each parse unit passed to it, starting at the topmost JPNode node in the tree. It checks to see if each node is a FieldRefNode referencing the reportField we are interested in:

private void walkTree(JPNode node) throws Exception {
      if (node==null) return;
      boolean match = false;
      if (node instanceof FieldRefNode) {
           Symbol symbol = node.getSymbol();
           if (        symbol!=null
               && symbol instanceof FieldBuffer
               && ((FieldBuffer)symbol).getField() == reportField
               )
               match = true;
      }
      if (match) {
           printReference(node);
      } else {
           walkTree(node.firstChild());
      }
      walkTree(node.nextSibling());
}

In printReference(), the beginning of the statement is found, and the first one node or two (DISPLAY, ASSIGN, etc.) is displayed, followed by the ellipses "...". The parent of the FieldRefNode is then found, and everything below it is then printed (including, of course, the FieldRefNode itself).

If you are new to syntax trees, then you might want to have a look at joanju.com/dist/docs/parse_trees to get an idea why print2() has to take an extra step to make sure that binary operators are printed in line.