For a project at university, I need to parse a GML file. GML files are XML based so I use JDOM2 to parse it. To fit my purposes, I extended org.jdom2.Document like so:
package datenbank;
import java.io.File;
// some more imports
public class GMLDatei extends org.jdom2.Document {
public void saveAsFile() {
// ...
}
public GMLKnoten getRootElement(){
return (GMLKnoten) this.getDocument().getRootElement();
}
public void setRootElement(GMLKnoten root){
this.getDocument().setRootElement(root);
}
}
I also extended org.jdom2.Element and named the subclass GMLKnoten but this does not matter too much for my question.
When testing, I try to load a GML file. When using the native document and element classes, it loads fine, but when using my subclasses, I get the following scenario:
I load the file using:
SAXBuilder saxBuilder = new SAXBuilder();
File inputFile = new File("gml/Roads_Munich_Route_Lines.gml");
GMLDatei document = null;
ArrayList<String> types = new ArrayList<String>();
try {
document = (GMLDatei) saxBuilder.build(inputFile);
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
In the line
document = (GMLDatei) saxBuilder.build(inputFile);
I get a Cast-Exception:
Exception in thread "main" java.lang.ClassCastException: org.jdom2.Document cannot be cast to datenbank.GMLDatei
at datenbank.GMLTest.main(GMLTest.java:27)
I thought that casting schould be no problem as I am subclassing org.jdom2.document. What am I missing?
vat
In general I want to "challenge" your requirement to extend Document - what value do you get from your custom classes that are not already part of the native implementation? I ask this for 2 reasons:
as the maintainer of JDOM, should I be adding some new feature?
I am just curious.....
JDOM has a system in place for allowing you to extend it's core classes and have a different implementation of them when parsing a document. It is done by extending the JDOMFactory.
Consider this code here: JDOMFactory interface. When SAXParser parses a document it uses those methods to build the document.
There is a default, overridable implementation in DefaultJDOMFactory that you can extend, and, for example, in your implementation, you must override the non-final "Element" methods like:
#Override
public Element element(final int line, final int col, final String name,
String prefix, String uri) {
return new Element(name, prefix, uri);
}
and instead have:
#Override
public Element element(final int line, final int col, final String name,
String prefix, String uri) {
return new GMLKnoten (name, prefix, uri);
}
Note that you will have to override all methods that are non-final and return content that is to be customised (for example, you will have to override 4 Element methods by my count.
With your own GMLJDOMFactory you can then use SAXBuilder by either using the full constructor new SAXBuilder(null, null, new GMPJDOMFactory()) or by setting the JDOMFactory after you have constructred it with setJDOMFactory(...)
Related
I'm trying to simply add some blank lines to my jdom xml output. I've tried the following without luck:
Element root = new Element("root");
root.addContent(new CDATA("\n"));
root.addContent(new Text("\n"));
I figured the all-whitespace entry was being ignored so I tried creating my own XMLOutputProccessor like this:
class TweakedOutputProcessor extends AbstractXMLOutputProcessor {
#Override
public void process(java.io.Writer out, Format format, Text text) throws IOException {
if ("\n".equals(text.getText())) {
out.write("\n");
} else {
super.process(out, format, text);
}
}
}\
... called like this:
public static void printDocument(Document doc) {
XMLOutputter xmlOutput = new XMLOutputter(new TweakedOutputProcessor());
xmlOutput.setFormat(Format.getPrettyFormat());
try {
xmlOutput.output(doc, System.out);
} catch (IOException e) { }
}
The unexpected thing here was that process(..., Text) was never called. After some experimentation I've found that process(..., Document) is being called, but none of the other process(..., *) methods are.
I also tried overriding the printText(...) and printCDATA(...) methods, but neither is being called -- even when the text is non-whitespace! Yet printElement(...) is being called.
So...
What is going on here? What's doing the work if not these methods?
How do I simply insert a blank line?
Use the XML xml:space="preserve" when setting values in the XML. JDOM honours that XML white space handling
Am new to ANTLR framework. I am working with parsing a Java file. Using ANTLR I am generating the JavaLexer, JavaParser, JavaListener, JavaBaseListener uning org.antlr.v4.Tool
Here I have an issue. I create a class that overrides the required methods of JavaBaseListener
Here is the code:
JavaMetaDataReader.java
public class JavaMetaDataReader extends JavaBaseListener{
#Override
public void enterAnnotation(AnnotationContext ctx)
{
System.out.println(ctx.getText());
}
#Override
public void enterAnnotationName(AnnotationNameContext ctx)
{
System.out.println(ctx.getText());
}
#Override
public void enterElementValuePairs(ElementValuePairsContext ctx)
{
System.out.println(ctx.getText());
System.out.println("Parent: "+ctx.getParent().getText());
}
}
JavaReader.Java //Contains the main method.
public class JavaReader {
public static void main(String[] args) {
File fileTobeRead = new File("./src/main/java/sample/HelloWord.java");
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(fileTobeRead);
ANTLRInputStream input = new ANTLRInputStream(fileInputStream);
JavaLexer lexer = new JavaLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
JavaParser parser = new JavaParser(tokens);
ParseTree tree = parser.compilationUnit(); // parse
ParseTreeWalker walker = new ParseTreeWalker(); // create standard walker
JavaMetaDataReader javaMetaDataReader = new JavaMetaDataReader();
walker.walk(javaMetaDataReader, tree); // initiate walk of tree with listener
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
HelloWorld.java
public class HelloWord {
#SuppressWarnings(value = "Helloo")
private void helloWorld() {
// TODO Auto-generated method stub
}
}
Here HelloWorld.java is the file that needs to be parsed.
Here am trying to access the annotations.
The enterAnnotation prints: #SuppressWarnings(value="Helloo")
The enterAnnotationName prints: SuppressWarnings
The first statement in enterElementValuePair prints: value="Helloo"
The second statement in enterElementValuePair prints: Parent: #SuppressWarnings(value="Helloo")
But i need it to print SuppressWarnings (the valure printed by enterAnnotationName
I don't know where I went wrong. I need to access the annotationName inside enterElementValuePairs.
What should I do?
Kindly help me as I am a beginner.
You didn't include a copy of the grammar, so I can only guess about the relation between your rules. It seems the parent of elementValuePairs is annotation, not annotationName. You need to first access the parent (AnnotationContext), and then call AnnotationContext.annotationName() to get the previous sibling of the ElementValuePairsContext.
If you are using the unofficial "optimized" release of the Java target, you can add the following annotation to your enterElementValuePairs method to ensure that a compiler error is reported if you later use the elementValuePairs rule in a way which would break this. Note that this requires the #version{} action to be used when the grammar is modified, as described in the link below.
#RuleDependencies({
#RuleDependency(recognizer=JavaParser.class, rule=JavaParser.RULE_elementValuePairs,
version=0, dependents={Dependents.PARENTS, Dependents.DESCENDANTS}),
#RuleDependency(recognizer=JavaParser.class, rule=JavaParser.RULE_annotation,
version=0, dependents=Dependents.SELF),
#RuleDependency(recognizer=JavaParser.class, rule=JavaParser.RULE_annotationName,
version=0, dependents=Dependents.DESCENDANTS)
})
More information about using rule dependencies is available here:
https://github.com/sharwell/antlr4/wiki/Rule-Versioning
I'm really newbie to groovy scripting but following some tutorial I tried to dynamically load some groovy class within my java code using parseClass() method of GroovyClassLoader.
I wrote some snippet and it worked fine for me. The problem is that I don't clearly understand what groovy engine is doing beyond my view and how those scripts are compiled?
Does a new class gets creted and loaded into jvm? Or does my application uses some cached sources?
Here is the class I'm trying to parse:
private static class MyScript {
#Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("public class SomeClass\n");
builder.append("{\n");
builder.append("Some code...").append("\n");
builder.append("}\n");
return builder.toString();
}
The I load it with build() as below:
private Class MyGroovyBuilder {
private Script script = new Script();
public String build() throws TemplateCompilationException
//
String groovyText = script.toString();
//
CompilerConfiguration config = new CompilerConfiguration();
//
byte[] bytes;
try
{
bytes = groovyText.getBytes(config.getSourceEncoding());
}
catch (UnsupportedEncodingException e)
{
throw new TemplateCompilationException(e, groovyText);
}
//
InputStream in = new ByteArrayInputStream(bytes);
GroovyCodeSource gcs = new GroovyCodeSource(in, "SomeName", "/groovy/shell");
GroovyClassLoader loader = new
GroovyClassLoader(Thread.currentThread().getContextClassLoader(), config);
Class<?> scriptClass;
try
{
scriptClass = loader.parseClass(gcs, false);
}
catch (CompilationFailedException e)
{
throw new GroovyCompilationException(e, "SomeName", groovyText);
}
catch (ClassFormatError e)
{
throw new GroovyCompilationException(e, "SomeName", groovyText);
}
return scriptClass.getName();
}
Any clarification is greatelly appreciated.
BR.
After loading class it appears in your class loader, and can be accessed like any other class.
There is a simple tutorial [here], that show how to load class from string.
In simplest case, you can load class, and hold it's Class object, using it to create objects dynamically. For field access or method invokation you can rely on Groovy dynamic nature.
There is no "cached source" or smth like that behind the scene and you can forget, from where your class is loaded. You can also cache classes, that are already compiled, and save them somewhere, as described [here]. It will drastically improve performance, if you need to load same class often.
But it will be better, to dig down in topic, because dynamic class loading is advanced Java/Groovy technique, it's whole infrastructure of chained classloaders, so it's better to refer documentation about them.
Links below may be helpful.
http://javarevisited.blogspot.ru/2012/12/how-classloader-works-in-java.html
How to use URLClassLoader to load a *.class file?
my system is both jibx and a legacy xml app and i want to build a constructor that can take a string of xml and unmarshal it into its own class. like this:
public ActiveBankTO(String xmlIn)
{
try
{
ByteArrayInputStream bin = new ByteArrayInputStream(xmlIn.getBytes());
IBindingFactory bfact;
bfact = BindingDirectory.getFactory(ActiveBankTO.class);
IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
this = (ActiveBankTO) uctx.unmarshalDocument(bin, null);
} catch (JiBXException e)
{
e.printStackTrace();
}
}
but obviously i cant assign "this" as a variable. is there a way to make this work? i realize i can put this into a static method that can be used, or a few other tricks to make it work, but this is something that has come up on several projects in various forms and i was wondering if this particular method is possible.
No, it's not possible. The static method solution is the best idea.
public static ActiveBankTO parseActiveBankTO(String xmlIn) {
ActiveBankTO newTO = null;
try {
ByteArrayInputStream bin = new ByteArrayInputStream(xmlIn.getBytes());
IBindingFactory bfact;
bfact = BindingDirectory.getFactory(ActiveBankTO.class);
IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
newTO = (ActiveBankTO) uctx.unmarshalDocument(bin, null);
} catch (JiBXException e) {
e.printStackTrace();
}
return newTO;
}
No. ti's not possible in the constructor. A static factory method is the only real way (you can't even cheat like this in bytecode).
There are a lot of examples on the internet of "reading" files but I can't find anything on "editing" a node value and writing it back out to the original file.
I have a non-working xml writer class that looks like this:
import org.w3c.dom.Document;
public class RunIt {
public static Document xmlDocument;
public static void main(String[] args)
throws TransformerException, IOException {
try {
xmlDocument = DocumentBuilderFactory.newInstance()
.newDocumentBuilder().parse("thor.xml");
} catch (IOException ex) {
ex.printStackTrace();
} catch (SAXException ex) {
ex.printStackTrace();
} catch (ParserConfigurationException ex) {
ex.printStackTrace();
}
addElement("A", "New");
writeDoc();
}
public static void addElement(String path, String val){
Element e = xmlDocument.createElement(path);
e.appendChild(xmlDocument.createTextNode(val));
xmlDocument.getDocumentElement().appendChild(e);
}
public static void writeDoc() throws TransformerException, IOException {
StringWriter writer = new StringWriter();
Transformer tf;
try {
tf = TransformerFactory.newInstance().newTransformer();
tf.transform(new DOMSource(xmlDocument), new StreamResult(writer));
writer.close();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerFactoryConfigurationError e) {
e.printStackTrace();
}
}
}
For this example, lets say this is the XML and I want to add a "C" node (inside the A node) that contains the value "New" :
<A>
<B>Original</B>
</A>
You use the Document object to create new nodes. Adding nodes as you suggest involves creating a node, setting its content and then appending it to the root element. In this case your code would look somehting like this:
Element e = xmlDocument.createElement("C");
e.appendChild(xmlDocument.createTextNode("new"));
xmlDocument.getDocumentElement().appendChild(e);
This will add the C node as a new child of A right after the B node.
Additionally, Element has some convenience functions that reduce the amount of required code. The second line above could have been replaced with
e.setTextContent("new");
More complicated efforts involving non root elements will involve you using XPath to fetch the target node to be edited. If you do start to use XPath to target nodes, bear in mind that the JDK XPath performance is abysmal. Avoid using an XPath of "#foo" in favor of constructs like e.getAttribute("foo") whenever you can.
EDIT: Formatting the document back to a string which can be written to a file can be done with the following code.
Document xmlDocument;
StringWriter writer = new StringWriter();
TransformerFactory.newInstance().transform(new DOMSource(xmlDocument), new StreamResult(writer));
writer.close();
String xmlString = writer.toString();
EDIT: Re: updated question with code.
Your code doesn't work because you're conflating 'path' and 'element name'. The parameter to Document.createElement() is the name of the new node, not the location in which to place it. In the example code I wrote I didn't get into locating the appropriate node because you were asking specifically about adding a node to the document parent element. If you want your addElement() to behave the way I think you're expecting it to behave, you'd have to add another parameter for the xpath of the target parent node.
The other problem with your code is that your writeDoc() function doesn't have any output. My example shows writing the XML to a String value. You can write it to any writer you want by adapting the code, but in your example code you use a StringWriter but never extract the written string out of it.
I would suggest rewriting your code something like this
public static void main(String[] args) {
File xmlFile = new File("thor.xml");
Document xmlDocument = DocumentBuilderFactory.newInstance()
.newDocumentBuilder().parse(xmlFile);
// this is effective because we know we're adding to the
// document root element
// if you want to write to an arbitrary node, you must
// include code to find that node
addTextElement(xmlDocument.getDocumentElement(), "C", "New");
writeDoc(new FileWriter(xmlFile);
}
public static Element addTextElement(Node parent, String element, String val){
Element e = addElement(parent, element)
e.appendChild(xmlDocument.createTextNode(val));
return e;
}
public static Element addElement(Node parent, String element){
Element e = xmlDocument.createElement(path);
parent.appendChild(e);
return e;
}
public static void writeDoc(Writer writer) {
try {
Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.transform(new DOMSource(xmlDocument), new StreamResult(writer));
} finally {
writer.close();
}
}
In order to write your document back to a file, you'll need an XML serializer or write your own. If you are using the Xerces library, check out XMLSerializer. For sample usage, you can also check out the DOMWriter samples page.
For more information on Xerces, read this