/** | |
* Copyright (c) 2008 INRIA. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* INRIA - initial API and implementation | |
* | |
* $Id: PrettyPrinter.java,v 1.7 2008/07/02 18:33:39 fjouault Exp $ | |
*/ | |
package org.eclipse.gmt.tcs.extractor; | |
import java.io.OutputStream; | |
import java.text.DecimalFormat; | |
import java.text.DecimalFormatSymbols; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Stack; | |
/** | |
* @author Frédéric Jouault | |
* @author Mikaël Barbero | |
* | |
*/ | |
public class PrettyPrinter { | |
private Map templates = new HashMap(); | |
private Map primitiveTemplates = new HashMap(); | |
private Map tokens = new HashMap(); | |
private Collection keywords = new ArrayList(); | |
private boolean kwCheckIgnoreCase; | |
private String identEscStart = "\""; | |
private String identEscEnd = "\""; | |
private String stringDelim = "\'"; | |
private boolean serializeComments = true; | |
private boolean usePrimitiveTemplates = false; | |
private static DecimalFormatSymbols dfs = new DecimalFormatSymbols(); | |
private DecimalFormat df = new DecimalFormat("0.##############", dfs); | |
private TCSExtractorStream out; | |
private Stack priorities = new Stack(); | |
private Stack currentSeparator = new Stack(); | |
private int indentLevel = 0; | |
private String indentString = " "; | |
private String curIndent = ""; | |
private String standardSeparator = " "; | |
private String lineFeed = "\n"; | |
private ModelAdapter modelAdapter; | |
public void prettyPrint(Object source, ModelAdapter ma, OutputStream target, Map arguments) { | |
this.modelAdapter = ma; | |
out = (TCSExtractorStream)arguments.get("stream"); | |
if(out == null) | |
out = new TCSExtractorPrintStream(target); | |
String newIndentString = (String)arguments.get("indentString"); | |
String newStandardSeparator = (String)arguments.get("standardSeparator"); | |
kwCheckIgnoreCase = "true".equals(arguments.get("kwCheckIgnoreCase")); | |
debug = "true".equals(arguments.get("debug")); | |
debugws = "true".equals(arguments.get("debugws")); | |
serializeComments = !"false".equals(arguments.get("serializeComments")); | |
usePrimitiveTemplates = "true".equals(arguments.get("usePrimitiveTemplates")); | |
String identEscStart = (String)arguments.get("identEscStart"); | |
if(identEscStart != null) { | |
this.identEscStart = identEscStart; | |
} | |
String identEscEnd = (String)arguments.get("identEscEnd"); | |
if(identEscEnd != null) { | |
this.identEscEnd = identEscEnd; | |
} | |
String identEsc = (String)arguments.get("identEsc"); | |
if(identEsc != null) { | |
this.identEscStart = identEsc; | |
this.identEscEnd = identEsc; | |
} | |
String stringDelim = (String)arguments.get("stringDelim"); | |
if(stringDelim != null) { | |
this.stringDelim = stringDelim; | |
} | |
String decimalFormat = (String)arguments.get("decimalFormat"); | |
if(decimalFormat != null) { | |
this.df = new DecimalFormat(decimalFormat, dfs); | |
} | |
if(newIndentString != null) | |
indentString = newIndentString; | |
if(newStandardSeparator != null) | |
standardSeparator = newStandardSeparator; | |
Object format = arguments.get("format"); | |
List rootTemplates = new ArrayList();; | |
for(Iterator i = modelAdapter.getElementsByType(format, "Template").iterator() ; i.hasNext() ; ) { | |
Object ame = i.next(); | |
String name = this.modelAdapter.getString(ame, "name"); | |
boolean isMain = false; | |
if("ClassTemplate".equals(this.modelAdapter.getTypeName(ame))) { | |
isMain = this.modelAdapter.getBool(ame, "isMain"); | |
} | |
if(isMain) { | |
rootTemplates.add(ame); | |
} | |
if(this.modelAdapter.getTypeName(ame).equals("EnumerationTemplate")) { | |
Map mappings = new HashMap(); | |
for(Iterator j = this.modelAdapter.getCol(ame, "mappings") ; j.hasNext() ; ) { | |
Object mapping = j.next(); | |
mappings.put(this.modelAdapter.getString(this.modelAdapter.getME(mapping, "literal"), "name"), this.modelAdapter.getME(mapping, "element")); | |
} | |
templates.put(name, mappings); | |
} else if(this.modelAdapter.getTypeName(ame).equals("PrimitiveTemplate")) { | |
primitiveTemplates.put(name, ame); | |
name = this.modelAdapter.getString(ame, "typeName"); | |
Collection c = (Collection)templates.get(name); | |
if(c == null) { | |
c = new ArrayList(); | |
templates.put(name, c); | |
} | |
c.add(ame); | |
} else { | |
templates.put(name, ame); | |
} | |
} | |
for(Iterator i = modelAdapter.getElementsByType(format, "Keyword").iterator() ; i.hasNext() ; ) { | |
Object ame = i.next(); | |
String value = this.modelAdapter.getString(ame, "value"); | |
if(kwCheckIgnoreCase) | |
value = value.toUpperCase(); | |
keywords.add(value); | |
} | |
for(Iterator i = modelAdapter.getElementsByType(format, "Token").iterator() ; i.hasNext() ; ) { | |
Object ame = i.next(); | |
String name = this.modelAdapter.getString(ame, "name"); | |
tokens.put(name, ame); | |
//TODO: | |
// if("COMMENT".equals(name)) { | |
// this.modelAdapter.getCol(this.modelAdapter.getME(ame, "pattern"), "simplePatterns"); | |
// AMN.isa(ame, ""); | |
// commentToken = ame; | |
// } | |
} | |
for(Iterator i = modelAdapter.getElementsByType(format, "Symbol").iterator() ; i.hasNext() ; ) { | |
Object ame = i.next(); | |
String value = this.modelAdapter.getString(ame, "value"); | |
debug("Symbol: " + value); | |
int type = TYPE_SYMBOL; | |
for(Iterator j = this.modelAdapter.getCol(ame, "spaces") ; j.hasNext() ; ) { | |
String l = this.modelAdapter.getEnumLiteralName(j.next()); | |
debug("\tLiteral: " + l); | |
if(l.equals("leftSpace")) | |
type += SYMBOL_LS; | |
else if(l.equals("leftNone")) | |
type += SYMBOL_LN; | |
if(l.equals("rightSpace")) | |
type += SYMBOL_RS; | |
else if(l.equals("rightNone")) | |
type += SYMBOL_RN; | |
} | |
symbols.put(value, new Integer(type)); | |
} | |
List pr = new ArrayList(); | |
boolean isMulti = false; | |
for(Iterator i = rootTemplates.iterator() ; i.hasNext() ;) { | |
Object rootTemplate = i.next(); | |
String rootName = this.modelAdapter.getString(rootTemplate, "name"); | |
pr.addAll(this.modelAdapter.getElementsByType(source, rootName)); | |
isMulti |= this.modelAdapter.getBool(rootTemplate, "isMulti"); | |
} | |
Iterator possibleRoots = pr.iterator(); | |
boolean first = true; | |
while(possibleRoots.hasNext()) { | |
Object root = possibleRoots.next(); | |
if(this.modelAdapter.isAModelElement(this.modelAdapter.refImmediateComposite(root))) | |
continue; // not a real root | |
if((!isMulti) && (!first)) { | |
System.out.println("Error: multiple possible roots found."); | |
break; | |
} | |
priorities.push(new Integer(Integer.MAX_VALUE)); | |
//pushSep(" "); | |
serialize(root); | |
first = false; | |
} | |
if(first && !isMulti) { | |
System.out.println("Error: no root found."); | |
} | |
out.close(); | |
} | |
private void pushSep(String sep) { | |
currentSeparator.push(sep); | |
debug("PUSHING SEPARATOR: \"" + sep + "\""); | |
} | |
private void popSep() { | |
String old = (String)currentSeparator.pop(); | |
debug("POPING SEPARATOR: \"" + old + "\""); | |
} | |
private void serialize(Object ame) { | |
pushSep(standardSeparator); | |
String typeName = this.modelAdapter.getTypeName(ame); | |
debug("processing " + typeName); | |
Object template = templates.get(typeName); | |
if(template == null) { | |
throw new TCSExtractionException("cannot find mathing template for: " + typeName, null); | |
} | |
String templateTypeName = this.modelAdapter.getTypeName(template); | |
debug("Applying template type " + templateTypeName); | |
if(serializeComments) { | |
try { | |
boolean first = true; | |
boolean nl = false; | |
for(Iterator i = this.modelAdapter.getCol(ame, "commentsBefore") ; i.hasNext() ; ) { | |
String c = this.modelAdapter.nextString(i); | |
if(c.equals("\n")) { | |
nl = true; | |
} else { | |
debug("printing comment: \"" + c + "\""); | |
if(first && !nl) { | |
printComment(c); | |
//out.print("SHOULD-BE-NONL"); | |
} else { | |
printComment(c); | |
} | |
printWS(lineFeed + curIndent); | |
first = false; | |
} | |
} | |
} catch(Exception e) { | |
System.out.println("Warning: could not get comments of " + ame + ", disabling further comments serialization"); | |
serializeComments = false; | |
} | |
} | |
if(templateTypeName.equals("ClassTemplate")) { | |
priorities.push(new Integer(Integer.MAX_VALUE)); | |
serializeSeq(ame, this.modelAdapter.getME(template, "templateSequence")); | |
priorities.pop(); | |
} else if(templateTypeName.equals("OperatorTemplate")) { | |
String sourcePropName = this.modelAdapter.getString(template, "source"); | |
String opPropName = this.modelAdapter.getString(template, "storeOpTo"); | |
String rightPropName = this.modelAdapter.getString(template, "storeRightTo"); | |
debug("OperatorTemplate: source = " + sourcePropName + " ; operator = " + opPropName + " ; right = " + rightPropName); | |
Object r = null; | |
boolean isPostfix = false; // only valid for unary operators | |
boolean isUnary = false; | |
if(rightPropName != null) { | |
r = this.modelAdapter.get(ame, rightPropName); | |
if(r instanceof Collection) { | |
isUnary = (((Collection)r).size() == 0); | |
} else { | |
isUnary = r == null; | |
} | |
} else { | |
isUnary = true; | |
} | |
debug("rightPropName = " + rightPropName + " ; isUnary = " + isUnary); | |
Object operator = null; | |
if(opPropName != null) { | |
String op = this.modelAdapter.getString(ame, opPropName); | |
if (op == null) { | |
throw new RuntimeException("Property " + opPropName + " has not been set in " + ame + " (" + this.modelAdapter.getMetaobject(ame) + ")"); | |
} | |
for(Iterator i = this.modelAdapter.getCol(template, "operators") ; i.hasNext() && (operator == null) ; ) { | |
Object opme = i.next(); | |
Object literal = this.modelAdapter.getME(opme, "literal"); | |
String opmes = null; | |
if(literal == null) | |
opmes = ""; | |
else | |
opmes = this.modelAdapter.getString(literal, "value"); | |
int arity = this.modelAdapter.getInt(opme, "arity"); | |
if(op.equals(opmes)) { | |
if(rightPropName != null) { | |
if((isUnary && (arity == 1)) || | |
((!isUnary) && (arity == 2))) { | |
operator = opme; | |
} | |
} else { | |
operator = opme; | |
isPostfix = this.modelAdapter.getBool(opme, "isPostfix"); | |
} | |
} | |
} | |
if(operator == null) { | |
System.err.println("Error: could not find operator \"" + op + "\""); | |
} | |
} else { | |
operator = this.modelAdapter.getCol(template, "operators").next(); | |
isUnary = this.modelAdapter.getInt(operator, "arity") == 1; | |
if(isUnary) { | |
isPostfix = this.modelAdapter.getBool(operator, "isPostfix"); | |
} | |
} | |
int curPrio = ((Integer)priorities.peek()).intValue(); | |
int priority = this.modelAdapter.getInt(this.modelAdapter.getME(operator, "priority"), "value"); | |
boolean paren = priority > curPrio; | |
priorities.push(new Integer(priority)); | |
Object literal = this.modelAdapter.getME(operator, "literal"); | |
debug("PRIORITY = " + priority + " ; CURPRIO = " + curPrio + " ; OPERATOR = " + ((literal != null) ? this.modelAdapter.getString(literal, "value") : "") + " ; paren = " + paren); | |
if(paren) | |
printSymbol("("); | |
Object source = this.modelAdapter.getME(ame, sourcePropName); | |
if(isUnary) { | |
if(isPostfix) { | |
serialize(source); | |
if(literal != null) | |
printLiteral(literal); | |
} else { | |
if(literal != null) | |
printLiteral(literal); | |
serialize(source); | |
} | |
} else { | |
serialize(source); | |
if(literal != null) | |
printLiteral(literal); | |
} | |
Object seq = this.modelAdapter.getME(template, "otSequence"); | |
if(rightPropName == null) { | |
priorities.push(new Integer(Integer.MAX_VALUE)); | |
serializeSeq(ame, seq); | |
priorities.pop(); | |
} else { | |
if(seq != null) | |
serializeSeq(ame, seq); | |
if(r instanceof Collection) { | |
for(Iterator i = ((Collection)r).iterator() ; i.hasNext() ; ) { | |
serialize(i.next()); | |
} | |
} else { | |
if(!isUnary) | |
serialize(r); | |
} | |
} | |
priorities.pop(); | |
if(paren) | |
printSymbol(")"); | |
} else { | |
error("unsupported template type: " + templateTypeName); | |
} | |
if(serializeComments) { | |
try { | |
for(Iterator i = this.modelAdapter.getCol(ame, "commentsAfter") ; i.hasNext() ; ) { | |
String c = this.modelAdapter.nextString(i); | |
if(c.equals("\n")) { | |
} else { | |
printComment(c); | |
printWS(lineFeed + curIndent); | |
} | |
} | |
} catch(Exception e) { | |
System.out.println("Warning: could not get comments of " + ame + ", disabling further comments serialization"); | |
serializeComments = false; | |
} | |
} | |
popSep(); | |
} | |
private void serializeSeq(Object ame, Object seq) { | |
if(seq != null) { | |
for(Iterator i = this.modelAdapter.getCol(seq, "elements") ; i.hasNext() ; ) { | |
Object e = i.next(); | |
serializeSeqElem(ame, e); | |
} | |
} | |
} | |
private String getLineFeeds(int n) { | |
String ret = ""; | |
for(int i = 0 ; i < n ; i++) { | |
ret += lineFeed; | |
} | |
return ret; | |
} | |
/** | |
* | |
* @param element Context model element of which to serialize a part (from source model). | |
* @param seqElem SequenceElement specifying a part of element to serialize. | |
*/ | |
private void serializeSeqElem(Object element, Object seqElem) { | |
String tn = this.modelAdapter.getTypeName(seqElem); | |
debug("serializing seq elem " + tn); | |
if(tn.equals("LiteralRef")) { | |
Object literal = this.modelAdapter.getME(seqElem, "referredLiteral"); | |
printLiteral(literal); | |
} else if(tn.equals("CustomSeparator")) { | |
String name = this.modelAdapter.getString(seqElem, "name"); | |
if(name.equals("no_space")) { | |
typeLast = TYPE_SYMBOL + SYMBOL_RN; | |
} else if(name.equals("space")) { | |
printWS(" "); | |
} else if(name.equals("newline")) { | |
printWS(lineFeed); printWS(curIndent); | |
} else if(name.equals("tab")) { | |
printWS("\t"); | |
} | |
} else if(tn.equals("Property")) { | |
Object v = this.modelAdapter.get(element, this.modelAdapter.getString(seqElem, "name")); | |
serializeProperty(element, v, seqElem); | |
} else if(tn.equals("Block")) { | |
if(debugws) out.debug("<block>"); | |
Object nbNLBArg = getBArg(this.modelAdapter, seqElem, "NbNL"); | |
Object startNbNLBArg = getBArg(this.modelAdapter, seqElem, "StartNbNL"); | |
Object indentIncrBArg = getBArg(this.modelAdapter, seqElem, "IndentIncr"); | |
Object startNLBArg = getBArg(this.modelAdapter, seqElem, "StartNL"); | |
Object endNLBArg = getBArg(this.modelAdapter, seqElem, "EndNL"); | |
int indentIncr = 1; | |
int nbNL = 1; | |
boolean startNL = true; | |
boolean endNL = true; | |
if(nbNLBArg != null) { | |
nbNL = this.modelAdapter.getInt(nbNLBArg, "value"); | |
} | |
int startNbNL = nbNL; // by default, startNbNL = nbNL | |
if(startNbNLBArg != null) { | |
startNbNL = this.modelAdapter.getInt(startNbNLBArg, "value"); | |
} | |
if(indentIncrBArg != null) { | |
indentIncr = this.modelAdapter.getInt(indentIncrBArg, "value"); | |
} | |
if(startNLBArg != null) { | |
startNL = this.modelAdapter.getBool(startNLBArg, "value"); | |
} | |
if(endNLBArg != null) { | |
endNL = this.modelAdapter.getBool(endNLBArg, "value"); | |
} | |
debug("nbNL = " + nbNL + " ; indentIncr = " + indentIncr); | |
indentLevel += indentIncr; | |
for(int i = 0 ; i < indentIncr ; i++) { | |
curIndent += indentString; | |
} | |
String nls = getLineFeeds(nbNL); | |
/*TODO: this was removed because of ATL filter but why was it necessary? | |
if(!" ".equals(currentSeparator.peek())) { | |
printWS(); | |
} | |
*/ | |
pushSep(nls + ((nbNL == 0) ? standardSeparator : curIndent)); | |
if(debugws) out.debug("<BeforeFirstWS/>"); | |
if(startNL) { | |
if(startNbNL == 0) { | |
printWS(""); | |
} else if(nbNL == startNbNL) { | |
printWS(); | |
} else { | |
printWS(getLineFeeds(startNbNL) + curIndent); | |
} | |
} else { | |
if(debugws) out.debug("<BeforeNonStartNLWS/>"); | |
printWS(""); // to make sure the last item was a TYPE_SPACE | |
if(debugws) out.debug("<AfterNonStartNLWS/>"); | |
} | |
if(debugws) out.debug("<blockContent>"); | |
serializeSeq(element, this.modelAdapter.getME(seqElem, "blockSequence")); | |
if(debugws) out.debug("</blockContent>"); | |
indentLevel -= indentIncr; | |
curIndent = curIndent.substring(0, curIndent.length() - indentString.length() * indentIncr); | |
if(endNL) { | |
printWS(lineFeed + curIndent); | |
} | |
popSep(); | |
if(debugws) out.debug("</block>"); | |
} else if(tn.equals("FunctionCall")) { | |
serializeSeq(element, this.modelAdapter.getME(this.modelAdapter.getME(seqElem, "calledFunction"), "functionSequence")); | |
} else if(tn.equals("ConditionalElement")) { | |
Object condition = this.modelAdapter.getME(seqElem, "condition"); | |
if(eval(element, condition)) { | |
Object tseq = this.modelAdapter.getME(seqElem, "thenSequence"); | |
if(tseq != null) printWSBlockNoDup(); | |
serializeSeq(element, tseq); | |
} else { | |
Object eseq = this.modelAdapter.getME(seqElem, "elseSequence"); | |
debug("ELSE SEQ = " + eseq); | |
if(eseq != null) printWSBlockNoDup(); | |
serializeSeq(element, eseq); | |
} | |
} else if(tn.equals("Alternative")) { | |
warning("alternatives are serialized following their first branch"); | |
Object seq = this.modelAdapter.getCol(seqElem, "sequences").next(); | |
serializeSeq(element, seq); | |
} else { | |
error("unsupported: " + tn); | |
} | |
} | |
private boolean eval(Object context, Object condition) { | |
boolean ret = true; | |
String ctn = this.modelAdapter.getTypeName(condition); | |
if(ctn.equals("AndExp")) { | |
ret = true; | |
for(Iterator i = this.modelAdapter.getCol(condition, "expressions") ; i.hasNext() ; ) { | |
ret &= eval(context, i.next()); | |
} | |
} else if(ctn.equals("BooleanPropertyExp")) { | |
ret = this.modelAdapter.getBool(context, this.modelAdapter.getString(condition, "propertyName")); | |
} else if(ctn.equals("IsDefinedExp")) { | |
Object val = this.modelAdapter.get(context, this.modelAdapter.getString(condition, "propertyName")); | |
if (val == null) { | |
ret = false; | |
} else if(val instanceof Collection) { | |
ret = (((Collection)val).size() > 0); | |
} else { | |
ret = true; | |
} | |
if(this.modelAdapter.getString(condition, "propertyName").equals("superRule")) | |
debug("!!!superRule: " + ret + " " + val); | |
} else if(ctn.equals("OneExp")) { | |
Object val = this.modelAdapter.get(context, this.modelAdapter.getString(condition, "propertyName")); | |
if (val == null) { | |
ret = false; | |
} else if(val instanceof Collection) { | |
ret = (((Collection)val).size() == 1); | |
} else { | |
ret = true; | |
} | |
if(this.modelAdapter.getString(condition, "propertyName").equals("superRule")) | |
debug("!!!superRule: " + ret + " " + val); | |
} else if(ctn.equals("EqualsExp")) { | |
Object value = this.modelAdapter.getME(condition, "value"); | |
String vtn = this.modelAdapter.getTypeName(value); | |
if(vtn.equals("IntegerVal")) { | |
int lv = this.modelAdapter.getInt(value, "symbol"); | |
int pv = this.modelAdapter.getInt(context, this.modelAdapter.getString(condition, "propertyName")); | |
ret = (lv == pv); | |
} else if(vtn.equals("NegativeIntegerVal")) { | |
int lv = -this.modelAdapter.getInt(value, "symbol"); | |
int pv = this.modelAdapter.getInt(context, this.modelAdapter.getString(condition, "propertyName")); | |
ret = (lv == pv); | |
} else if(vtn.equals("StringVal")) { | |
String lv = this.modelAdapter.getString(value, "symbol"); | |
String pv = this.modelAdapter.getString(context, this.modelAdapter.getString(condition, "propertyName")); | |
ret = (lv.equals(pv)); | |
} else if(vtn.equals("EnumLiteralVal")) { | |
String lv = this.modelAdapter.getString(value, "name"); | |
String pv = this.modelAdapter.getString(context, this.modelAdapter.getString(condition, "propertyName")); | |
ret = (lv.equals(pv)); | |
} else { | |
error(vtn + " unsupported."); | |
} | |
//TODO: PropertyVal | |
} | |
return ret; | |
} | |
private void serializeProperty(Object element, Object value, Object property) { | |
if(value == null) return; | |
if(value instanceof Collection) { | |
Object sep = getPArg(this.modelAdapter, property, "Separator"); | |
if(sep != null) sep = this.modelAdapter.getME(sep, "separatorSequence"); | |
boolean first = true; | |
for(Iterator i = ((Collection)value).iterator() ; i.hasNext() ; ) { | |
if(first) { | |
printWSBlockNoDup(); | |
first = false; | |
} else { | |
if(typeLast != TYPE_SPACE) | |
printWS(); | |
} | |
serializeProperty(element, i.next(), property); | |
if(i.hasNext()) { | |
if(sep != null) { | |
serializeSeq(element, sep); | |
} | |
} | |
} | |
} else if(this.modelAdapter.isEnumLiteral(value)) { | |
//error("enumeration literals cannot be properly serialized at the present time (" + enumName + ")"); | |
String enumName = this.modelAdapter.getName(this.modelAdapter.getPropertyType(this.modelAdapter.getMetaobject(element), this.modelAdapter.getName(property))); | |
Map mappings = (Map)templates.get(enumName); | |
Object seqElem = mappings.get(this.modelAdapter.getEnumLiteralName(value)); | |
serializeSeqElem(element, seqElem); | |
} else if (this.modelAdapter.isAModelElement(value)) { | |
printWSBlockNoDup(); | |
Object refersTo = getPArg(this.modelAdapter, property, "RefersTo"); | |
if(refersTo == null) { | |
serialize(value); | |
} else { | |
Object v = this.modelAdapter.get(value, this.modelAdapter.getString(refersTo, "propertyName")); | |
Object asp = getPArg(this.modelAdapter, property, "As"); | |
String as = null; | |
if(asp != null) { | |
as = this.modelAdapter.getString(asp, "value"); | |
} | |
serializePrimitive(v, as); | |
} | |
} else if(this.modelAdapter.isPrimitive(value)) { | |
printWSBlockNoDup(); | |
Object asp = getPArg(this.modelAdapter, property, "As"); | |
String as = null; | |
if(asp != null) { | |
as = this.modelAdapter.getString(asp, "value"); | |
} | |
serializePrimitive(value, as); | |
} else { | |
error("unsupported " + ((value == null) ? null : value.getClass())); | |
} | |
} | |
private void serializePrimitive(Object value, String as) { | |
if(value instanceof String) { | |
boolean doDefault = true; | |
if(usePrimitiveTemplates) { | |
Collection c = (Collection)templates.get("String"); | |
Object t = null; | |
for(Iterator i = c.iterator() ; i.hasNext() && (t == null) ; ) { | |
Object ame = i.next(); | |
if((as == null) && this.modelAdapter.getBool(ame, "isDefault")) { | |
t = ame; | |
} else if(this.modelAdapter.getString(ame, "name").equals(as)) { | |
t = ame; | |
} | |
} | |
if(t == null) { | |
System.out.println("warning: no primitive template found for String" + ((as == null) ? "" : "as " + as)); | |
} else { | |
String tokenName = this.modelAdapter.getString(t, "tokenName"); | |
Object token = tokens.get(tokenName); | |
if(token != null) { | |
debug("Token found: " + tokenName); | |
Object pattern = this.modelAdapter.getME(token, "pattern"); | |
String patternTypeName = this.modelAdapter.getTypeName(pattern); | |
if(patternTypeName.equals("OrPattern")) { | |
pattern = this.modelAdapter.getCol(pattern, "simplePatterns").next(); // TODO: warning if more than one? | |
patternTypeName = this.modelAdapter.getTypeName(pattern); | |
} | |
if(patternTypeName.equals("RulePattern")) { | |
pattern = this.modelAdapter.getME(pattern, "rule"); | |
patternTypeName = this.modelAdapter.getTypeName(pattern); | |
} | |
if(patternTypeName.equals("MultiLineRule")) { | |
// String regex = "^" + buildRegex(pattern) + "$"; | |
// System.out.println(regex); | |
// String val = (String)value; | |
// System.out.println(val); | |
// System.out.println(val.matches(regex)); | |
Object start = this.modelAdapter.getME(pattern, "start"); | |
Object end = this.modelAdapter.getME(pattern, "end"); | |
Object esc = this.modelAdapter.getME(pattern, "esc"); | |
if( | |
this.modelAdapter.getTypeName(start).equals("StringPattern") | |
&& | |
this.modelAdapter.getTypeName(end).equals("StringPattern") | |
) { | |
printDisambiguationWS(); | |
if(esc != null && this.modelAdapter.getTypeName(esc).equals("StringPattern")) { | |
if("\\".equals(this.modelAdapter.getString(esc, "name"))) { | |
value = cString((String)value); | |
} | |
} | |
out.printEscapedIdentifier(this.modelAdapter.getString(start, "name"), (String)value, this.modelAdapter.getString(end, "name")); | |
typeLast = TYPE_IDENT; | |
doDefault = false; | |
} else { | |
System.out.println("warning: only supporting StringPattern for start end end of MultilineRule"); | |
} | |
} else { | |
System.out.println("warning: no support for " + patternTypeName + " yet"); | |
} | |
} | |
} | |
} | |
if(doDefault) { | |
if("stringSymbol".equals(as)) { | |
printStringLiteral(cString((String)value)); | |
} else { | |
Object template = primitiveTemplates.get(as); | |
boolean orKeyword = false; | |
if(template != null) | |
orKeyword = this.modelAdapter.getBoolUndefinedIsFalse(template, "orKeyword"); | |
printIdentifier((String)value, orKeyword); | |
} | |
} | |
} else if(value instanceof Integer) { | |
printIntegerLiteral(((Integer)value).intValue()); | |
} else if(value instanceof Double) { | |
printRealLiteral(((Double)value).doubleValue()); | |
} else if(value instanceof Boolean) { | |
printBooleanLiteral(((Boolean)value).booleanValue()); | |
} | |
} | |
private static String cString(String s) { | |
StringBuffer ret = new StringBuffer(); | |
for(int i = 0 ; i < s.length() ; i++) { | |
char c = s.charAt(i); | |
if(c == '\n') { | |
ret.append("\\n"); | |
} else if(c == '\r') { | |
ret.append("\\r"); | |
} else if(c == '\t') { | |
ret.append("\\t"); | |
} else if(c == '\b') { | |
ret.append("\\b"); | |
} else if(c == '\f') { | |
ret.append("\\f"); | |
} else if((c < ' ') || ((c > '~') && (c < '¡'))) { | |
ret.append("\\"); | |
if(c < 010) | |
ret.append("0"); | |
if(c < 0100) | |
ret.append("0"); | |
ret.append(java.lang.Integer.toOctalString(c)); | |
} else if(c == '\'') { | |
ret.append("\\'"); | |
} else if(c == '\"') { | |
ret.append("\\\""); | |
} else if(c == '\\') { | |
ret.append("\\\\"); | |
} else { | |
ret.append(c); | |
} | |
} | |
return "" + ret; | |
} | |
private String buildRegex(Object pattern) { | |
String ret = null; | |
String typeName = this.modelAdapter.getTypeName(pattern); | |
if(typeName.equals("OrPattern")) { | |
boolean paren = false; | |
for(Iterator i = this.modelAdapter.getCol(pattern, "simplePatterns") ; i.hasNext() ; ) { | |
Object p = i.next(); | |
if(ret == null) { | |
ret = buildRegex(p); | |
} else { | |
paren = true; | |
ret += "|" + buildRegex(p); | |
} | |
} | |
if(paren) ret = "(" + ret + ")"; | |
} else if(typeName.equals("RulePattern")) { | |
Object rule = this.modelAdapter.getME(pattern, "rule"); | |
String rtn = this.modelAdapter.getTypeName(rule); | |
if(rtn.equals("WordRule")) { | |
ret = "("; | |
ret += buildRegex(this.modelAdapter.getME(rule, "start")); | |
ret += buildRegex(this.modelAdapter.getME(rule, "part")) + "*"; | |
Object end = this.modelAdapter.getME(rule, "end"); | |
if(end != null) { | |
ret += buildRegex(end); | |
} | |
ret += ")"; | |
// } else if(rtn.equals("EndsOfLineRule")) { | |
} else if(rtn.equals("MultiLineRule")) { | |
ret = "("; | |
//startIsNotKept ret += buildRegex(getME(rule, "start")); | |
String endRegex = buildRegex(this.modelAdapter.getME(rule, "end")); | |
String middleRegex = "[^" + endRegex + "]"; | |
Object esc = this.modelAdapter.getME(rule, "esc"); | |
if(esc != null) { | |
String escRegex = buildRegex(esc); | |
middleRegex = "(" + middleRegex + "|" + escRegex + ".)"; | |
} | |
ret += middleRegex + "*"; | |
//endIsNotKept ret += endRegex; | |
ret += ")"; | |
} else { | |
error("unsupported rule type: " + rtn); | |
} | |
} else if(typeName.equals("StringPattern")) { | |
String name = this.modelAdapter.getString(pattern, "name"); | |
ret = name | |
.replaceAll("\\\\", "\\\\\\\\") | |
.replaceAll("\\*", "\\\\*") | |
.replaceAll("\\{", "\\\\{") | |
.replaceAll("\\}", "\\\\}") | |
.replaceAll("\\(", "\\\\(") | |
.replaceAll("\\)", "\\\\)") | |
.replaceAll("\\+", "\\\\+"); | |
} else if(typeName.equals("ClassPattern")) { | |
String name = this.modelAdapter.getString(pattern, "name"); | |
ret = "\\p{" + ("" + name.charAt(0)).toUpperCase() + name.substring(1) + "}"; | |
} else { | |
error("unsupported pattern type: " + typeName); | |
} | |
return ret; | |
} | |
// Low-level serialization | |
private final static int TYPE_KEYWORD = 1; | |
private final static int TYPE_SYMBOL = 2; | |
private final static int TYPE_IDENT = 3; | |
private final static int TYPE_BOOL = 4; | |
private final static int TYPE_INT = 5; | |
private final static int TYPE_REAL = 6; | |
private final static int TYPE_STRING = 7; | |
private final static int TYPE_SPACE = 8; | |
private final static int TYPE_COMMENT = 9; | |
private final static int SYMBOL_LS = 16; // symbol with a space before (on the Left) | |
private final static int SYMBOL_RS = 32; // symbol with a space after (on the Right) | |
private final static int SYMBOL_BS = SYMBOL_LS + SYMBOL_RS; // symbol with spaces Both before and after | |
private final static int SYMBOL_LN = 64; // symbol with no space before (even if preceding symbol is RS or BS) | |
private final static int SYMBOL_RN = 128; // symbol with no space after (even if following symbol is LS or BS) | |
private int typeLast = 0; | |
private void printLiteral(Object literal) { | |
String s = this.modelAdapter.getString(literal, "value"); | |
String ltn = this.modelAdapter.getTypeName(literal); | |
if(ltn.equals("Keyword")) { | |
printKeyword(s); | |
} else { | |
printSymbol(s); | |
} | |
} | |
private void printWS(String ws) { | |
debug("printing WS = \"" + ws + "\""); | |
out.printWhiteSpace(ws); | |
typeLast = TYPE_SPACE; | |
//new Exception().printStackTrace(out); | |
} | |
private void printWS() { | |
printWS((String)currentSeparator.peek()); | |
} | |
private boolean isSymbol(int type, int test) { | |
test += TYPE_SYMBOL; | |
return (type & test) == test; | |
} | |
private void printDisambiguationWS() { | |
if((typeLast == TYPE_KEYWORD) || | |
(typeLast == TYPE_IDENT) || | |
(typeLast == TYPE_INT) || | |
(typeLast == TYPE_REAL) || | |
(typeLast == TYPE_STRING) || | |
(typeLast == TYPE_BOOL) || | |
(isSymbol(typeLast, SYMBOL_BS)) || | |
(isSymbol(typeLast, SYMBOL_RS))) { | |
// out.print("<typeLast=" + typeLast + ">"); | |
printWS(); | |
} | |
} | |
private void printWSBlockNoDup() { | |
if(typeLast != TYPE_SPACE) | |
if(!currentSeparator.peek().equals(" ")) | |
printWS(); | |
} | |
private void printWSNoDup() { | |
if(typeLast != TYPE_SPACE) | |
printWS(); | |
} | |
private void printKeyword(String keyword) { | |
printDisambiguationWS(); | |
out.printKeyword(keyword); | |
typeLast = TYPE_KEYWORD; | |
} | |
private Map symbols = new HashMap(); | |
private void printSymbol(String symbol) { | |
Integer type = (Integer)symbols.get(symbol); | |
int typeCurrent = -1; | |
if(type == null) { | |
typeCurrent = TYPE_SYMBOL; | |
} else { | |
typeCurrent = type.intValue(); | |
} | |
if( | |
((isSymbol(typeCurrent, SYMBOL_LS) || isSymbol(typeCurrent, SYMBOL_BS)) && !isSymbol(typeLast, SYMBOL_RN)) || | |
((isSymbol(typeLast, SYMBOL_RS) || isSymbol(typeLast, SYMBOL_BS)) && !isSymbol(typeCurrent, SYMBOL_LN)) | |
) { | |
printWSNoDup(); | |
} | |
out.printSymbol(symbol); | |
typeLast = typeCurrent; | |
} | |
// private void printIdentifier(String ident) { | |
// printIdentifier(ident, false); | |
// } | |
private void printIdentifier(String ident, boolean orKeyword) { | |
printDisambiguationWS(); | |
boolean simpleIdent = ident.matches("[_a-zA-Z][_a-zA-Z0-9]*"); | |
if(simpleIdent && !orKeyword) | |
simpleIdent = !keywords.contains(ident); | |
if((!orKeyword) && kwCheckIgnoreCase && keywords.contains(ident.toUpperCase())) { | |
simpleIdent = false; | |
} | |
if(simpleIdent) { | |
out.printIdentifier(ident); | |
} else { | |
out.printEscapedIdentifier(identEscStart, cString(ident), identEscEnd); | |
} | |
typeLast = TYPE_IDENT; | |
} | |
private void printBooleanLiteral(boolean v) { | |
printDisambiguationWS(); | |
out.printBoolean(v); | |
typeLast = TYPE_BOOL; | |
} | |
private void printIntegerLiteral(int v) { | |
printDisambiguationWS(); | |
out.printInteger(v); | |
typeLast = TYPE_INT; | |
} | |
static { | |
dfs.setDecimalSeparator('.'); | |
} | |
private void printRealLiteral(double v) { | |
printDisambiguationWS(); | |
out.printReal(df.format(v)); // TODO: format properly | |
typeLast = TYPE_REAL; | |
} | |
private void printStringLiteral(String v) { | |
printDisambiguationWS(); | |
out.printString(stringDelim, v); | |
typeLast = TYPE_STRING; | |
} | |
private void printComment(String c) { | |
printDisambiguationWS(); | |
out.printComment(c); | |
typeLast = TYPE_COMMENT; | |
} | |
// Source model navigation helpers. | |
private static Object getPArg(ModelAdapter ma, Object ame, String name) { | |
Object ret = null; | |
for(Iterator i = ma.getCol(ame, "propertyArgs") ; i.hasNext() && (ret == null) ; ) { | |
Object arg = i.next(); | |
if(ma.getTypeName(arg).equals(name + "PArg")) | |
ret = arg; | |
} | |
return ret; | |
} | |
private static Object getBArg(ModelAdapter ma, Object ame, String name) { | |
Object ret = null; | |
for(Iterator i = ma.getCol(ame, "blockArgs") ; i.hasNext() && (ret == null) ; ) { | |
Object arg = i.next(); | |
if(ma.getTypeName(arg).equals(name + "BArg")) | |
ret = arg; | |
} | |
return ret; | |
} | |
private boolean debug = false; | |
private boolean debugws = false; | |
private void debug(String msg) { | |
if(debug) | |
System.out.println(msg); | |
} | |
private void error(String msg) { | |
System.out.println("ERROR: " + msg); | |
} | |
private void warning(String msg) { | |
System.out.println("WARNING: " + msg); | |
} | |
} |