|
Case Study: PropertiesThis is a little case study that shows how the ASTcentric Framework can be used.
The aim of this case study is to replace a Java properties file by an AST. A Java class ( Table of Content:
For the purpose of this case study such ASTs are too simple because they are simple trees and not ASGs (i.e. Abstract Syntax Graphs) with references between nodes. To make properties ASTs real ASGs we introduce the feature that the value of a key-value pair can contain a reference to another key-value pair. There are extensions of host = astcentric.sourceforge.net port = 8080 baseURL = http://${host}:${port}Note the syntactical elements as =, ${, and } which structures such text-based properties files. In addition some escaping syntax has to be defined for these elements. This is a typcial example of a domain specific language (DSL). One of the great hopes in AST-centric progamming is that it should become easier to define and mix DSLs (see Martin Fowler: Language Workbenches: The Killer-App for Domain Specific Languages?). The root node has for each key-value pair a child node again with the key as the name of the node. The value is the concatenation of the children of a key-value node. There are two types of value nodes: Plain Value nodes with a plain string value and Value Reference nodes which refer to key-value nodes in the AST. The example from the last chapter will look in the plain AST editor like
All green nodes in Fig. 2 are of type Validator which is a node of the AST Validation Declaration. This AST is part of the ASTcentric distribution. It defines the current default AST Validation Language. Unfolding the green nodes reveals their definition in terms of this language which is of course another DSL. The AST Validation Language is rather complex. The is currently no good documentation because this language is still not finished. In order to understand some of its elements all four major nodes of Properties Specification are briefly discussed. They are not discussed in order they appear in the AST but in the order from simple to complex. There are two child nodes. Each refer to a node of the AST Validation Declaration and define a condition. A node is a valid node of type Plain Value if it fulfills all conditions. They are
This is expressed by the node of the type Children Composition. Such a node has one or many nodes of the type Children. The child nodes of a Children node define conditions a child of the node to be validated has to fulfill in order to be counted. The UML-like multiplicity specifies the valid range of counts. A Children Composition is valid when its sequence of Children definitions is valid. It is typical that a Children has a child of type has reference. Again its children specify conditions which have to be fulfilled by the node referred by the node to be validated. Here it has to be a member of a set specified by the from set node. If the value of the from set node is false it is forbidden to be a member of the set. The children of a from set node define node collections. The set is the union of these collections. Here there is only one child. It is a Node Collection node: The collection is defined by the references of its children. The first condition says that all names of the children of the node to be validated have to be unique. The children of type Unique Name define node collections. The condition is checked for the union of these collections. A collections of type descendent of specifies descendent nodes either of the node for which the Unique Name condition applies or for the collection specified by its second child. The first child specifies the degree of relationship.The second condition is very similar to the second condition in Fig. 4: It says that a node of type Properties has arbitray many children. All children have to by of type Property. A node of this type has exactly one child which has a reference from the set defined by all older siblings of the Property node which is the grandparent of that child.Two nested ancestors of nodes both of type parent are used to express a grandparent relationship. To create Properties ASTs one has to download astcentric-example-properties.jar. It contains the AST Properties Specification described in Sec. 2.
java -cp astcentric-example-properties.jar:astcentric-editor.jar:astcentric.jar \ astcentric.editor.swing.plain.ASTEditorThe following steps describe partially how to create the AST shown in Fig. 1. For a general introduction of editing an AST see Creating an AST. Here only the differences caused by the Properties Specification is discussed.
java.util.Properties object needs the class PropertiesGenerator from astcentric-example-properties.jar.It is used in the following code to print a Properties AST onto the console:
1: import java.io.*; 2: import java.util.*; 3: 4: import astcentric.example.properties.PropertiesGenerator; 5: import astcentric.structure.basic.*; 6: import astcentric.structure.filesystem.RealFile; 7: 8: public class PropertiesASTPrinter 9: { 10: public static void main(String[] args) throws Exception 11: { 12: if (args.length == 0) 13: { 14: System.out.println("Usage: java PropertiesASTPrinter <AST file>"); 15: System.exit(1); 16: } 17: 18: ASTPath rootPath = new ASTPath(System.getProperty("java.class.path")); 19: ASTLoader rootLoader = new DefaultASTLoader(rootPath, true); 20: ASTPath path = new ASTPath(new RealFile(new File(args[0]))); 21: ASTLoader loader = new DefaultASTLoader(rootLoader, path, true); 22: List<ASTInfo> infos = ASTTool.getAllLoadableASTs(loader, false); 23: AST ast = loader.loadAST(infos.get(0).getID()); 24: Properties properties = PropertiesGenerator.generate(ast.getRoot()); 25: ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 26: properties.store(outputStream, "Created from AST " + ast.getInfo()); 27: System.out.println(new String(baos.toByteArray())); 29: } 30: }Applied to the AST of Fig. 1 would lead to #Created from AST astcentric.sourceforge.net*fjelmer*12csd6dh1 My first Properties AST #Sun Jun 17 16:10:28 CEST 2007 port=8080 host=astcentric.sourceforge.net baseURL=http\://astcentric.sourceforge.net\:8080Line 18 and 19 define the root AST loader which contains all ASTs found in the Java class path: 18: ASTPath rootPath = new ASTPath(System.getProperty("java.class.path")); 19: ASTLoader rootLoader = new DefaultASTLoader(rootPath, true);The two following lines define an AST loader only for the AST file of the command line argument. It has rootLoader as its parent loader.
20: ASTPath path = new ASTPath(new RealFile(new File(args[0]))); 21: ASTLoader loader = new DefaultASTLoader(rootLoader, path, true);The AST Properties Specification referred by a Properties AST will be loaded by the root loader. An AST is loaded by invocating the method loadAST() of the AST loader. The argument of this method is the ID of the AST to be loaded. To get the ID the informations of all ASTs loadable by this loader is obtained. Because there is only one AST there is no doubt which entry in the list of ASTInfo instances has to be asked for the ID:
22: List<ASTInfo> infos = ASTTool.getAllLoadableASTs(loader, false); 23: AST ast = loader.loadAST(infos.get(0).getID());Line 24 creates a Properties object from the AST by using PropertiesGenerator . The rest of the code deals with printing the Properties object on the console. AST ID and name are added as a comment.
Its only public method reads
1: public static Properties generate(Node propertiesNode) 2: { 3: Node reference = propertiesNode.getReference(); 4: if (reference == null) 5: { 6: throw new NodeException(propertiesNode, 7: "Reference to Properties Declaration missing"); 8: } 9: AST ast = reference.getAST(); 10: PropertiesDeclaration declaration = new PropertiesDeclaration(ast); 11: Properties properties = new Properties(); 12: ListIn line 12 all child nodes are obtained from the argument node. It follows a loop over them assuming each child is of type Property. In line 20 the method getValue() is invoked with this child node. The second argument is a helper class instanciated in line 10. It has the nodes of Property, Plain Value, and Value Reference. They are need to distinguish the different types of child nodes of a Property node (line 9, 13, 18):
1: private static String getValue(Node node, 2: final PropertiesDeclaration declaration) 3: { 4: Node reference = node.getReference(); 5: if (reference == null) 6: { 7: throw new NodeException(node, "Missing reference."); 8: } 9: if (NodeTool.same(reference, declaration.plainValueNode)) 10: { 11: return node.getValue().toString(); 12: } 13: if (NodeTool.same(reference, declaration.valueReferenceNode)) 14: { 15: Node ref = NodeTool.getNodesOf(node).get(0).getReference(); 16: return getValue(ref, declaration); 17: } 18: if (NodeTool.same(reference, declaration.propertyNode)) 19: { 20: final StringBuilder builder = new StringBuilder(); 21: node.traverseNodes(new NodeHandler() 22: { 23: public boolean handle(Node child) 24: { 25: builder.append(getValue(child, declaration)); 26: return false; 27: } 28: }); 29: return new String(builder); 30: } 31: throw new NodeException(node, "Isn't declared correctly: " + reference); 32: }In line 25 the method calls itself recursively in order to resolve references of references. Note, that in line 21 an inner iterator is used to go over all children of a Property node. |
(C) 2007 Franz-Josef Elmer. All rights reserved. Last modified: 6/17/2007 |