![]()
|
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?).
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.
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 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.
Two nested ancestors of nodes both of type parent are used to express a grandparent relationship.
java -cp astcentric-example-properties.jar:astcentric-editor.jar:astcentric.jar \ astcentric.editor.swing.plain.ASTEditor
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\:8080
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.
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 |