/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package cb.parser;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Stack;
import java.util.StringTokenizer;
import cb.petal.Design;
import cb.petal.List;
import cb.petal.Petal;
import cb.petal.PetalFile;
import cb.petal.PetalNode;
import cb.petal.PetalObject;
import cb.petal.StringLiteral;
import cb.petal.Value;
import cb.petal.Visitor;
/**
* Optimized parser for Rational Rose Petal files.
*
* @version $Id: PetalParser.java,v 1.3 2011/09/12 11:47:21 gpolet Exp $
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
*/
public class PetalParser {
private Lexer lexer;
private PetalNode current_parent = null;
private Stack parent_stack = new Stack(); // Stack<PetalNode>
private void saveParent(PetalNode new_parent) {
parent_stack.push(current_parent);
current_parent = new_parent;
}
private void restoreParent() {
current_parent = (PetalNode) parent_stack.pop();
}
private static ObjectFactory factory = ObjectFactory.getInstance();
public PetalParser(Reader r) {
lexer = new Lexer(r);
}
private java.util.List ignored_nodes = Collections.EMPTY_LIST;
/**
* If the parser finds such a node while building the petal tree, the node will be ignored and not added to the tree. E.g,
* setIgnoredNodes(Diagram.class) will abandon all diagrams of the model.
*/
public void setIgnoredNodes(java.lang.Class[] nodes) {
ignored_nodes = new ArrayList(Arrays.asList(nodes));
}
public java.lang.Class[] getIgnoredNodes() {
java.lang.Class[] nodes = new java.lang.Class[ignored_nodes.size()];
ignored_nodes.toArray(nodes);
return nodes;
}
private boolean ignored(PetalNode obj) {
for (Iterator i = ignored_nodes.iterator(); i.hasNext();) {
java.lang.Class clazz = (java.lang.Class) i.next();
if (clazz.isInstance(obj)) {
return true;
}
}
return false;
}
private Token match(int kind, String match) {
Token t = lexer.getToken();
if (t.kind != kind) {
throw new RuntimeException("Mismatch: Expected " + kind + " but got " + t.kind + " at line " + t.line);
}
if (match != null && !match.equals(t.image)) {
throw new RuntimeException("Mismatch: Expected " + match + " but got " + t.image + " at line " + t.line);
}
return t;
}
private Token match(int kind) {
return match(kind, null);
}
private ArrayList list = new ArrayList(); // Reused list to collect docs
/**
* (...)* wildcard
*
* @return images
*/
private ArrayList matchAll(int kind) {
list.clear();
Token t = lexer.getToken();
while (t.kind == kind) {
list.add(t.image);
t = lexer.getToken();
}
lexer.ungetToken(t);
return list;
}
/**
* [...] optional
*
* @return image
*/
private Token matchAny(int kind) {
Token t = lexer.getToken();
if (t.kind == kind) {
return t;
} else {
lexer.ungetToken(t);
return null;
}
}
public static PetalParser createParser(String file_name) {
return createParser(new File(file_name));
}
public static PetalParser createParser(java.net.URL url) {
try {
return createParser(url.openStream());
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private String model_name = "anonymous";
public static PetalParser createParser(File file) {
try {
PetalParser parser = new PetalParser(new FileReader(file));
String name = file.getName();
int index = name.lastIndexOf('.');
if (index > 0) {
parser.model_name = name.substring(0, index);
}
parser.setCurrentDir(file);
return parser;
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
private File _current;
/**
* Set current, i.e., the directory where the source MDL file is located so that references to external .CAT files can be resolved.
*/
public void setCurrentDir(File dir) {
if (!dir.isDirectory()) {
dir = dir.getParentFile();
}
_current = dir;
}
/**
* Resolve reference to external file, e.g., "$CURDIR\\ConsolidatedView\\ConsolidatedView.cat"
*
* @return file handle or null if file can not be located
*/
public File resolveReference(String path) {
if (_current == null) {
System.err.println("Could not resolve reference to " + path + ", use parser.setCurrentDir()");
return null;
} else {
StringTokenizer st = new StringTokenizer(path, "\\/");
ArrayList list = new ArrayList();
while (st.hasMoreTokens()) {
list.add(st.nextToken());
}
StringBuffer new_path = new StringBuffer();
for (Iterator i = list.iterator(); i.hasNext();) {
String str = (String) i.next();
if (str.startsWith("$")) {
if (!str.startsWith("$CURDIR")) {
throw new RuntimeException("Unknown variable " + str);
} else {
str = _current.getPath();
}
}
new_path.append(str);
if (i.hasNext()) {
new_path.append(File.separatorChar);
}
}
return new File(new_path.toString());
}
}
public static PetalParser createParser(Reader stream) {
return new PetalParser(stream);
}
public static PetalParser createParser(InputStream stream) {
return new PetalParser(new InputStreamReader(stream));
}
/**
* Top level construct are always petal and design objects
*/
public PetalFile parse() {
PetalObject petal, design;
PetalFile file = new PetalFile();
current_parent = file;
petal = parseObject();
design = parseObject();
file.setPetal((Petal) petal);
file.setDesign((Design) design);
file.setModelName(model_name);
return file;
}
/* Example: (object ClassView "Class" "Use Case View::Student" @76
* location (160, 176))
*/
public PetalObject parseObject() {
match(Lexer.LPAREN);
Token.dispose(match(Lexer.IDENT, "object"));
Token t1 = match(Lexer.IDENT);
ArrayList docs = matchAll(Lexer.STRING);
Token t3 = matchAny(Lexer.TAG);
PetalNode parent = ignored(current_parent) ? null : current_parent;
PetalObject obj = factory.createObject(parent, t1.image, docs, t3 == null ? null : t3.image);
saveParent(obj);
/* List of properties
*/
Token t4 = matchAny(Lexer.IDENT);
while (t4 != null) {
PetalNode prop = parseValue(false);
if (prop != null) {
obj.addProperty(t4.image, prop);
}
t4 = matchAny(Lexer.IDENT);
}
match(Lexer.RPAREN);
restoreParent();
Token.dispose(t1);
Token.dispose(t3);
Token.dispose(t4);
String cat = obj.getPropertyAsString("file_name");
/**
* Load any external .cat file and "paste" it in
*/
if (cat != null) {
File file = resolveReference(cat);
if (file != null && file.exists()) {
PetalParser p = PetalParser.createParser(file);
p.current_parent = parent;
p.parseObject();
return p.parseObject();
}
}
if (!ignored(obj)) {
obj.init();
return obj;
} else {
return null;
}
}
private static PetalNode RPAREN = new PetalNode() {
@Override
public String getKind() {
return null;
}
@Override
public int getChildCount() {
return 0;
}
@Override
public void accept(Visitor v) {
}
};
public PetalNode parseValue(boolean rparen_ok) {
Token t = lexer.getToken();
switch (t.kind) {
case Lexer.STRING:
Token.dispose(t);
return factory.createString(t.image, false);
case Lexer.MULTI_STRING:
Token.dispose(t);
return factory.createString(t.image, true);
case Lexer.INTEGER:
Token.dispose(t);
return factory.createInteger(t.image);
case Lexer.FLOAT:
Token.dispose(t);
return factory.createFloat(t.image);
case Lexer.BOOLEAN:
return factory.createBoolean(t.image);
case Lexer.TAG:
Token.dispose(t);
return factory.createTag(t.image);
case Lexer.LPAREN:
Token t2 = lexer.getToken();
switch (t2.kind) {
case Lexer.IDENT:
lexer.ungetToken(t2);
lexer.ungetToken(t);
if (t2.image.equals("object")) {
return parseObject();
} else if (t2.image.equals("list")) {
return parseList();
} else if (t2.image.equals("value")) {
return parseValueObject();
} else {
throw new RuntimeException("Unexpected " + t2.image + " after (");
}
case Lexer.INTEGER:
match(Lexer.COMMA);
Token t3 = match(Lexer.INTEGER);
match(Lexer.RPAREN);
return factory.createLocation(t2.image, t3.image);
case Lexer.STRING:
Token t4 = match(Lexer.INTEGER);
match(Lexer.RPAREN);
return factory.createTuple(t2.image, t4.image);
default:
throw new RuntimeException("Unexpected " + t2.image + "after (");
}
default:
if (t.kind == Lexer.RPAREN && rparen_ok) {
return RPAREN;
} else {
throw new RuntimeException("Unexpected " + t.image);
}
}
}
/* Example: (list unit_reference_list (object Module_Diagram "Main"
* quid "35CB163B03CF"))
*
*/
public List parseList() {
match(Lexer.LPAREN);
Token.dispose(match(Lexer.IDENT, "list"));
Token t = matchAny(Lexer.IDENT);
List list = factory.createList(t == null ? null : t.image);
Token.dispose(t);
PetalNode obj;
while ((obj = parseValue(true)) != RPAREN) {
if (obj != null) {
list.add(obj);
}
}
return list;
}
public Value parseValueObject() {
match(Lexer.LPAREN);
Token.dispose(match(Lexer.IDENT, "value"));
Token t = match(Lexer.IDENT);
Token t2 = lexer.getToken();
StringLiteral str;
switch (t2.kind) {
case Lexer.STRING:
str = factory.createString(t2.image, false);
break;
case Lexer.MULTI_STRING:
str = factory.createString(t2.image, true);
break;
default:
throw new RuntimeException("Unexpected " + t2.image + " in (value ...)");
}
match(Lexer.RPAREN);
Token.dispose(t);
Token.dispose(t2);
return factory.createValue(t.image, str);
}
public static void main(String[] args) {
parse(args);
}
/**
* Utility method for main
*/
public static PetalFile parse(String[] args) {
PetalParser parser;
if (args.length == 0) {
parser = PetalParser.createParser(System.in);
} else {
try {
URL url = new URL(args[0]);
parser = PetalParser.createParser(url);
} catch (MalformedURLException e) {
parser = PetalParser.createParser(args[0]);
}
}
try {
return parser.parse();
} catch (Exception ex) {
System.out.println("Exception at " + parser.lexer.getLine());
ex.printStackTrace();
}
return null;
}
}