org.nuiton.jaxx.compiler.tools.jaxxcapture.handlers.ObjectHandler Maven / Gradle / Ivy
The newest version!
/*
* #%L
* JAXX :: Compiler
* %%
* Copyright (C) 2008 - 2024 Code Lutin, Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
package org.nuiton.jaxx.compiler.tools.jaxxcapture.handlers;
import org.nuiton.jaxx.compiler.JAXXCompiler;
import org.nuiton.jaxx.compiler.finalizers.JAXXCompilerFinalizer;
import org.nuiton.jaxx.compiler.java.JavaFileGenerator;
import org.nuiton.jaxx.compiler.tools.jaxxcapture.CapturedObject;
import org.nuiton.jaxx.compiler.tools.jaxxcapture.ContextNode;
import org.nuiton.jaxx.compiler.tools.jaxxcapture.JAXXCapture;
import org.nuiton.jaxx.compiler.tools.jaxxcapture.LiteralNode;
import org.nuiton.jaxx.compiler.tools.jaxxcapture.MethodNode;
import org.nuiton.jaxx.compiler.tools.jaxxcapture.PropertyNode;
import org.nuiton.jaxx.compiler.tools.jaxxcapture.ValueNode;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.awt.Container;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Stack;
public class ObjectHandler {
private static int count;
public static final String ATTRIBUTE_PROPERTY = "property";
protected CapturedObject createCapturedObject(String className, JAXXCapture capture) {
return new CapturedObject(this, className, capture);
}
// returns true if the tag has any "void" children
protected boolean processChildren(Element tag, Stack context, JAXXCapture capture) {
boolean result = false;
NodeList children = tag.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
Element innerTag = (Element) child;
if (innerTag.getTagName().equals(JAXXCompilerFinalizer.TYPE_VOID)) {
result = true;
}
evaluate(innerTag, context, capture);
}
}
return result;
}
protected void evaluateProperty(Element tag, Stack context, JAXXCapture capture) {
// determine containing object
CapturedObject contextObject = null;
for (int i = context.size() - 1; i >= 0; i--) {
if (context.get(i) instanceof CapturedObject) {
contextObject = (CapturedObject) context.get(i);
break;
}
}
assert contextObject != null;
String property = tag.getAttribute(ATTRIBUTE_PROPERTY);
if (!property.equals("actionCommand")) { // filter out actionCommand due to screwiness in XMLEncoder's handling of it
Object current = context.peek();
PropertyNode newContext = new PropertyNode(property);
context.push(newContext);
boolean voidChildren = processChildren(tag, context, capture);
ContextNode[] arguments = newContext.getArguments();
if (arguments.length == 1) {
if (current instanceof CapturedObject && arguments[0] instanceof ValueNode) // simple property assignment
{
((CapturedObject) current).setProperty(property, dataBindingEncode(String.valueOf(((ValueNode) arguments[0]).getValue())));
} else if (current instanceof CapturedObject && arguments[0] instanceof CapturedObject && ((CapturedObject) arguments[0]).isInlineable()) // simple data binding
{
((CapturedObject) current).setProperty(property, "{" + capture.getJavaCode(arguments[0]) + "}");
} else {
contextObject.setInlineable(false);
contextObject.appendScriptCode(capture.getJavaCode(context));
}
} else if (!voidChildren) {
contextObject.setInlineable(false);
contextObject.appendScriptCode(capture.getJavaCode(context));
}
assert context.peek() == newContext;
context.pop();
}
}
protected void evaluateAdd(CapturedObject contextObject, CapturedObject child, ContextNode constraints) {
contextObject.addChild(child, constraints);
}
protected void evaluateMethod(Element tag, Stack context, JAXXCapture capture) {
// determine containing object
CapturedObject contextObject = null;
for (int i = context.size() - 1; i >= 0; i--) {
if (context.get(i) instanceof CapturedObject) {
contextObject = (CapturedObject) context.get(i);
break;
}
}
assert contextObject != null;
try {
String methodName = tag.getAttribute("method");
MethodNode newContext = new MethodNode(methodName);
context.push(newContext);
boolean voidChildren = processChildren(tag, context, capture);
boolean add = false;
ContextNode[] arguments = newContext.getArguments();
if (methodName.equals("add") && arguments.length >= 1 && arguments[0] instanceof CapturedObject) {
Class> contextClass = Class.forName(contextObject.getClassName(), true, capture.getClassLoader());
if (Container.class.isAssignableFrom(contextClass)) {
add = true;
evaluateAdd(contextObject, (CapturedObject) arguments[0], null);
}
}
if (!voidChildren && !add) {
contextObject.appendScriptCode(capture.getJavaCode(context));
}
assert context.peek() == newContext;
context.pop();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
protected void evaluate(Element tag, Stack context, JAXXCapture capture) {
String tagName = tag.getTagName();
if (tagName.equals("object")) {
String fieldName = tag.getAttribute("field");
ContextNode currentNode = context.peek();
if (fieldName.length() > 0) {
try {
String className = tag.getAttribute("class");
Field field = Class.forName(className, true, capture.getClassLoader()).getField(fieldName);
Object value = field.get(null);
currentNode.addArgument(new LiteralNode(className + "." + fieldName, value));
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
currentNode.addArgument(capture.processObject(tag, context));
}
} else if (tagName.equals(JAXXCompilerFinalizer.TYPE_VOID)) {
String property = tag.getAttribute(ATTRIBUTE_PROPERTY);
if (property.length() > 0) {
evaluateProperty(tag, context, capture);
} else {
evaluateMethod(tag, context, capture);
}
} else if (tagName.equals("string")) {
context.peek().addArgument(new ValueNode(JAXXCapture.getText(tag)));
} else if (tagName.equals("boolean")) {
context.peek().addArgument(new ValueNode(Boolean.valueOf(JAXXCapture.getText(tag))));
} else if (tagName.equals("char")) {
context.peek().addArgument(new ValueNode(JAXXCapture.getText(tag).charAt(0)));
} else if (tagName.equals("short")) {
context.peek().addArgument(new ValueNode(Short.valueOf(JAXXCapture.getText(tag))));
} else if (tagName.equals("int")) {
context.peek().addArgument(new ValueNode(Integer.valueOf(JAXXCapture.getText(tag))));
} else if (tagName.equals("long")) {
context.peek().addArgument(new ValueNode(Long.valueOf(JAXXCapture.getText(tag))));
} else if (tagName.equals("float")) {
context.peek().addArgument(new ValueNode(Float.valueOf(JAXXCapture.getText(tag))));
} else if (tagName.equals("double")) {
context.peek().addArgument(new ValueNode(Double.valueOf(JAXXCapture.getText(tag))));
} else if (tagName.equals("null")) {
context.peek().addArgument(new ValueNode(null));
} else {
System.err.println("unsupported tag: " + tag.getTagName());
}
}
private static String dataBindingEncode(String value) {
return value.replaceAll("\\{", "\\\\{").replaceAll("\\}", "\\\\}");
}
public CapturedObject processObject(Element objectTag, Stack context, JAXXCapture capture) {
String className = objectTag.getAttribute("class");
if (className.length() > 0) {
CapturedObject capturedObject = createCapturedObject(className, capture);
context.push(capturedObject);
NodeList children = objectTag.getChildNodes();
String id = objectTag.getAttribute("id");
if (id.length() == 0 || capture.getCapturedObjects().containsKey(id)) {
id = "Auto" + ++count;
}
assert !capture.getCapturedObjects().containsKey(id);
capture.getCapturedObjects().put(id, capturedObject);
capturedObject.setProperty("id", id);
// process object's name before anything else
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) child;
if (element.getTagName().equals(JAXXCompilerFinalizer.TYPE_VOID) && element.getAttribute(ATTRIBUTE_PROPERTY).equals("name")) {
evaluate(element, context, capture);
String name = capturedObject.getProperty("name");
if (name != null && !capture.getCapturedObjects().containsKey(name)) {
capture.getCapturedObjects().put(name, capturedObject);
capturedObject.setProperty("id", name);
capturedObject.getProperties().remove("name");
}
}
}
}
// process remaining children
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) child;
if (!JAXXCompilerFinalizer.TYPE_VOID.equals(element.getTagName()) || !element.getAttribute(ATTRIBUTE_PROPERTY).equals("name")) {
evaluate(element, context, capture);
}
}
}
assert context.peek() == capturedObject;
context.pop();
return capturedObject;
} else {
CapturedObject result = capture.getCapturedObjects().get(objectTag.getAttribute("idref"));
if (result == null) {
throw new RuntimeException("Internal error: could not find tag with id " + objectTag.getAttribute("idref"));
}
result.setInlineable(false); // we have at least two references to it, and so can't inline it
return result;
}
}
private static String xmlEncode(String src) {
return src.replaceAll("'", "&").replaceAll("<", "<");
}
public String getXML(CapturedObject object, JAXXCapture capture) {
StringBuilder result = new StringBuilder();
result.append('<');
String className = object.getClassName();
if (className.startsWith("javax.swing.")) {
className = className.substring("javax.swing.".length());
}
result.append(className);
Map properties = object.getProperties();
for (Map.Entry e : properties.entrySet()) {
result.append(' ');
result.append(e.getKey());
result.append("='");
result.append(xmlEncode(e.getValue()));
result.append('\'');
}
ContextNode[] arguments = object.getArguments();
if (arguments != null && arguments.length > 0) {
result.append(" constructorParams='");
for (int j = 0; j < arguments.length; j++) {
if (j != 0) {
result.append(", ");
}
result.append(capture.getJavaCode(arguments[j]));
}
result.append('\'');
}
boolean tagClosed = false;
String children = getChildXML(object, capture);
String lineSeparator = JAXXCompiler.getLineSeparator();
if (children != null && children.length() > 0) {
if (!tagClosed) {
tagClosed = true;
result.append('>');
result.append(lineSeparator);
}
result.append(children);
}
String script = object.getScriptCode();
if (script != null && script.length() > 0) {
if (!tagClosed) {
tagClosed = true;
result.append('>');
result.append(lineSeparator);
}
result.append(" ");
result.append(lineSeparator);
}
if (tagClosed) {
result.append("");
result.append(className);
result.append('>');
} else {
result.append("/>");
}
return result.toString();
}
protected String getChildXML(CapturedObject object, JAXXCapture capture) {
StringBuilder result = new StringBuilder();
CapturedObject[] children = object.getChildren();
String lineSeparator = JAXXCompiler.getLineSeparator();
for (CapturedObject aChildren : children) {
if (!aChildren.isInlineable()) {
result.append(JavaFileGenerator.indent(aChildren.getXML(capture), 2, false, lineSeparator));
result.append(lineSeparator);
}
}
return result.toString();
}
}