package ru.csu.stan.java.ast.treetoxml.stax;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.List;
import javax.lang.model.type.TypeKind;
import javax.tools.JavaFileObject;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import ru.csu.stan.java.ast.core.ContentAssistant;
import ru.csu.stan.java.ast.core.TraversalHandler;
import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.Name;
/**
* Вывод AST в виде XML с использованием StAX.
*
* @author mz
*
*/
public class StaxXmlRepresentation implements TraversalHandler {
private static String NEW_LINE = "\n";
private static String TAB = " ";
private int offset;
private XMLStreamWriter writer;
private String projectRoot;
private StaxXmlRepresentation() {}
public static StaxXmlRepresentation getInstance(String projectRoot, String filename) throws FileNotFoundException, XMLStreamException{
StaxXmlRepresentation representation = new StaxXmlRepresentation();
XMLOutputFactory factory = XMLOutputFactory.newInstance();
File f = new File(filename);
FileOutputStream stream = new FileOutputStream(f);
representation.writer = factory.createXMLStreamWriter(stream, "UTF-8");
File root = new File(projectRoot);
representation.projectRoot = root.getAbsolutePath();
return representation;
}
@Override
public void onStartNode(JCTree node, String name, Position position) {
try {
writeOffset();
this.writer.writeStartElement(ContentAssistant.getNodeName(ContentAssistant.getTagNameByTreeElementKind(node.getKind())));
this.writer.writeAttribute("line", String.valueOf(position.getLineNumber()));
this.writer.writeAttribute("col", String.valueOf(position.getColumnNumber()));
this.offset++;
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onEmptyNodeName(String nodeName) {
try {
writeOffset();
this.writer.writeStartElement(nodeName);
this.offset++;
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onEndNode(JCTree node, String name, Position position) {
try {
this.offset--;
writeOffset();
this.writer.writeEndElement();
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onNullNode(String name) {
}
@Override
public void onStartNodesList(List<? extends JCTree> nodesList, String name) {
try {
writeOffset();
this.writer.writeStartElement(name);
this.offset++;
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onEndNodesList(List<? extends JCTree> nodesList, String name) {
try {
this.offset--;
writeOffset();
this.writer.writeEndElement();
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onNullNodesList(String name) {
}
@Override
public void onSymbolStart(Symbol symbol, String name) {
}
@Override
public void onSymbolEnd(Symbol symbol, String name) {
}
@Override
public void onNullSymbol(String name) {
}
@Override
public void onName(Name nameElement, String name) {
try {
writer.writeAttribute("name", String.valueOf(nameElement));
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onLiteral(Object value, Kind valueType) {
String type = null;
switch (valueType) {
case INT_LITERAL:
type = "int";
break;
case DOUBLE_LITERAL:
type = "double";
break;
case LONG_LITERAL:
type = "long";
break;
case CHAR_LITERAL:
type = "char";
break;
case STRING_LITERAL:
type = "string";
break;
case BOOLEAN_LITERAL:
type = "boolean";
break;
case FLOAT_LITERAL:
type = "float";
break;
case NULL_LITERAL:
type = "null";
break;
default:
type = "undefined";
}
try {
this.writer.writeAttribute("type", type);
// здесь могут быть любые значения, даже \u0000.
// TODO сейчас значение непечатаемых символов теряется
this.writer.writeAttribute("value", String.valueOf(value).replaceAll("\\P{Print}", ""));
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onType(Type type, String name) {
}
@Override
public void onFlags(long flags) {
try {
String[] flagArray = Flags.toString(flags).split(" ");
for (String flagName : flagArray){
writeOffset();
this.writer.writeStartElement("modifier");
this.writer.writeAttribute("name", flagName);
this.writer.writeEndElement();
}
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onPrimitiveType(TypeKind typeKind) {
try {
this.writer.writeAttribute("name", String.valueOf(typeKind).toLowerCase());
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onEmptyStatement() {
}
@Override
public void onBoundKind(BoundKind boundKind) {
}
@Override
public void onErrorOcured(Exception e) {
}
private void writeOffset(){
try {
writer.writeCharacters(NEW_LINE);
for (int i = 0; i < offset; i++)
this.writer.writeCharacters(TAB);
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void startDocument(){
try {
writer.writeStartDocument();
this.writer.writeCharacters(NEW_LINE);
this.writer.writeCharacters(NEW_LINE);
writer.writeStartElement("project");
this.offset++;
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void endDocument(){
try {
this.offset--;
writeOffset();
writer.writeEndElement();
this.writer.writeCharacters(NEW_LINE);
writer.writeEndDocument();
writer.close();
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onSourceFile(JavaFileObject sourceFile) {
try {
writer.writeAttribute("filename", getRelativeFileName(sourceFile.getName()));
} catch (XMLStreamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String getRelativeFileName(String filename){
return filename.substring(projectRoot.length());
}
}