All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.nuiton.jaxx.compiler.tools.jaxxcapture.JAXXCapture Maven / Gradle / Ivy
/*
* #%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;
import org.apache.commons.lang3.StringUtils;
import org.nuiton.jaxx.compiler.ClassMap;
import org.nuiton.jaxx.compiler.CompiledObject;
import org.nuiton.jaxx.compiler.JAXXCompiler;
import org.nuiton.jaxx.compiler.reflect.ClassDescriptor;
import org.nuiton.jaxx.compiler.reflect.ClassDescriptorHelper;
import org.nuiton.jaxx.compiler.tools.jaxxcapture.handlers.JTabbedPaneHandler;
import org.nuiton.jaxx.compiler.tools.jaxxcapture.handlers.ObjectHandler;
import org.nuiton.jaxx.compiler.tools.jaxxcapture.handlers.TableHandler;
import org.nuiton.jaxx.compiler.types.TypeManager;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JTabbedPane;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.beans.XMLEncoder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
public class JAXXCapture {
private static final ClassMap objectHandlers = new ClassMap<>();
static {
//TODO make a serviceLoader mecanism to allow inter-module loading
objectHandlers.put(ClassDescriptorHelper.getClassDescriptor(Object.class), new ObjectHandler());
objectHandlers.put(ClassDescriptorHelper.getClassDescriptor(JTabbedPane.class), new JTabbedPaneHandler());
try {
objectHandlers.put(ClassDescriptorHelper.getClassDescriptor("org.nuiton.jaxx.runtime.swing.Table"), new TableHandler());
} catch (ClassNotFoundException e) {
System.err.println(e);
}
}
private final Map sourceObjects = new HashMap<>();
private final Map capturedObjects = new HashMap<>();
private ClassLoader classLoader;
private int count;
private static class CaptureEventQueue extends EventQueue {
private final ClassLoader classLoader;
private CaptureEventQueue(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public void dispatchEvent(AWTEvent event) {
if (event.getID() == MouseEvent.MOUSE_PRESSED && ((MouseEvent) event).isControlDown()) {
Component target = ((MouseEvent) event).getComponent();
if (!(target instanceof Window)) {
target = SwingUtilities.getWindowAncestor(target);
}
if (target instanceof JFrame) {
target = ((JFrame) target).getContentPane();
} else if (target instanceof JDialog) {
target = ((JDialog) target).getContentPane();
}
if (target instanceof JWindow) {
target = ((JWindow) target).getContentPane();
}
if (target != null) {
Thread.currentThread().setContextClassLoader(classLoader);
JAXXCapture capture = new JAXXCapture(classLoader);
capture.applyNames(target);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
XMLEncoder encoder = new XMLEncoder(buffer);
encoder.writeObject(target);
encoder.close();
try {
System.err.println(new String(buffer.toByteArray()));
System.out.println(capture.convertToJAXX(new ByteArrayInputStream(buffer.toByteArray())));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
super.dispatchEvent(event);
}
}
private JAXXCapture(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public ClassLoader getClassLoader() {
return classLoader;
}
public Map getCapturedObjects() {
return capturedObjects;
}
private void applyNames(Component target) {
String name = target.getName();
if (name == null || sourceObjects.containsKey(name) || !CompiledObject.isValidID(name)) {
do {
name = "Object" + ++count;
} while (sourceObjects.containsKey(name));
}
target.setName(name);
assert !sourceObjects.containsKey(name) : "ID " + name + " is already registered";
sourceObjects.put(name, target);
if (target instanceof Container) {
Container container = (Container) target;
for (int i = 0; i < container.getComponentCount(); i++) {
applyNames(container.getComponent(i));
}
}
}
public static String getText(Element tag) { // NOT a safe general-purpose implementation!
return ((Text) tag.getChildNodes().item(0)).getData();
}
private String getArgumentsCode(ContextNode[] arguments) {
StringBuilder result = new StringBuilder();
result.append('(');
for (int i = 0; i < arguments.length; i++) {
if (i != 0) {
result.append(", ");
}
result.append(getJavaCode(arguments[i]));
}
result.append(')');
return result.toString();
}
public String getJavaCode(ContextNode node) {
StringBuilder result = new StringBuilder();
if (node instanceof PropertyNode) {
ContextNode[] arguments = node.getArguments();
result.append(arguments.length == 0 ? "get" : "set");
result.append(StringUtils.capitalize(((PropertyNode) node).getProperty()));
result.append(getArgumentsCode(arguments));
} else if (node instanceof MethodNode) {
result.append((((MethodNode) node).getMethodName()));
result.append(getArgumentsCode(node.getArguments()));
} else if (node instanceof CapturedObject) {
CapturedObject object = (CapturedObject) node;
if (object.isInlineable()) {
result.append("new ");
result.append(object.getClassName());
result.append(getArgumentsCode(node.getArguments()));
} else {
String id = object.getProperty("id");
assert id != null;
result.append(id);
}
} else if (node instanceof ValueNode) {
result.append(TypeManager.getJavaCode(((ValueNode) node).getValue()));
} else if (node instanceof LiteralNode) {
result.append(((LiteralNode) node).getJavaCode());
} else {
throw new IllegalArgumentException("unrecognized node type: " + node);
}
return result.toString();
}
// returns the best matching method for the specified argument types
private static Method getMethod(Class> target, String methodName, Class>[] arguments) {
try {
// use the package-private class java.beans.ReflectionUtils to resolve the method. This isn't 100% safe, but it's better than
// having to rewrite the resolution myself.
Class> reflectionUtils = Class.forName("java.beans.ReflectionUtils");
Method getMethod = reflectionUtils.getDeclaredMethod("getMethod", Class.class, String.class, Class[].class);
getMethod.setAccessible(true);
return (Method) getMethod.invoke(null, target, methodName, arguments);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// returns the best matching constructor for the specified argument types
private static Constructor> getConstructor(Class> target, Class>[] arguments) {
try {
// use the package-private class java.beans.ReflectionUtils to resolve the constructor. This isn't 100% safe, but it's better than
// having to rewrite the resolution myself.
Class> reflectionUtils = Class.forName("java.beans.ReflectionUtils");
Method getConstructor = reflectionUtils.getDeclaredMethod("getConstructor", Class.class, Class[].class);
getConstructor.setAccessible(true);
return (Constructor>) getConstructor.invoke(null, target, arguments);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private Object createInstance(CapturedObject object) {
try {
ContextNode[] argumentNodes = object.getArguments();
Object[] arguments = new Object[argumentNodes.length];
Class>[] argumentTypes = new Class>[argumentNodes.length];
for (int j = 0; j < argumentNodes.length; j++) {
if (argumentNodes[j] instanceof ValueNode) {
arguments[j] = ((ValueNode) argumentNodes[j]).getValue();
argumentTypes[j] = arguments[j] != null ? arguments[j].getClass() : null;
} else if (argumentNodes[j] instanceof CapturedObject) {
arguments[j] = createInstance((CapturedObject) argumentNodes[j]);
argumentTypes[j] = arguments[j] != null ? arguments[j].getClass() : null;
}
}
Constructor> constructor = getConstructor(Class.forName(object.getClassName(), true, classLoader), argumentTypes);
return constructor.newInstance(arguments);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String getJavaCode(Stack/**/ context) {
CapturedObject contextCapturedObject = (CapturedObject) context.get(0);
StringBuilder result = new StringBuilder();
int start = 1;
for (int i = context.size() - 1; i > 1; i--) {
if (context.get(i) instanceof CapturedObject) {
start = i;
contextCapturedObject = (CapturedObject) context.get(i);
break;
}
}
Object contextObject = sourceObjects.get(contextCapturedObject.getProperty("id"));
Class> contextClass = contextObject != null ? contextObject.getClass() : null;
for (int i = start; i < context.size(); i++) {
ContextNode node = (ContextNode) context.get(i);
if (contextObject != null && (node instanceof MethodNode || node instanceof PropertyNode)) {
// need to follow the call chain so we can insert typecasts as necessary
try {
String methodName;
ContextNode[] argumentNodes = node.getArguments();
if (node instanceof MethodNode) {
methodName = ((MethodNode) node).getMethodName();
} else {
methodName = (argumentNodes.length == 0 ? "get" : "set") + StringUtils.capitalize(((PropertyNode) node).getProperty());
}
Object[] arguments = new Object[argumentNodes.length];
Class>[] argumentTypes = new Class>[argumentNodes.length];
for (int j = 0; j < argumentNodes.length; j++) {
if (argumentNodes[j] instanceof ValueNode) {
arguments[j] = ((ValueNode) argumentNodes[j]).getValue();
argumentTypes[j] = arguments[j] != null ? arguments[j].getClass() : null;
} else if (argumentNodes[j] instanceof CapturedObject) {
arguments[j] = createInstance((CapturedObject) argumentNodes[j]);
argumentTypes[j] = arguments[j].getClass();
} else if (argumentNodes[j] instanceof LiteralNode) {
arguments[j] = ((LiteralNode) argumentNodes[j]).getValue();
argumentTypes[j] = arguments[j].getClass();
} else {
throw new IllegalArgumentException("unsupported argument type: " + argumentNodes[j]);
}
}
Method method = getMethod(contextClass, methodName, argumentTypes);
if (method == null) {
// could not find method in contextClass, must be defined in a subclass -- insert a typecast
result.insert(0, "((" + getOutputName(contextObject.getClass()) + ") ");
result.append(')');
method = getMethod(contextObject.getClass(), methodName, argumentTypes);
}
if (method == null) {
throw new RuntimeException("could not find method " + methodName + Arrays.asList(argumentTypes) + " in " + contextObject.getClass() + " (context: " + context + ")");
}
contextObject = method.invoke(contextObject, arguments);
contextClass = method.getReturnType();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
if (i > start) {
result.append('.');
}
result.append(getJavaCode(node));
}
return result + ";";
}
private String getOutputName(Class> c) {
return c.getName();
}
public CapturedObject processObject(Element objectTag, Stack context) {
String className = objectTag.getAttribute("class");
ObjectHandler handler;
if (className.length() > 0) {
try {
ClassDescriptor descriptor = ClassDescriptorHelper.getClassDescriptor(className, classLoader);
handler = (ObjectHandler) objectHandlers.get(descriptor);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
} else {
handler = (ObjectHandler) objectHandlers.get(ClassDescriptorHelper.getClassDescriptor(Object.class));
}
return handler.processObject(objectTag, context, this);
}
private synchronized String convertToJAXX(InputStream beansXML) throws IOException {
try {
Document document = JAXXCompiler.parseDocument(beansXML);
Element rootElement = document.getDocumentElement();
NodeList nodes = rootElement.getChildNodes();
Stack context = new Stack<>();
for (int i = 0; i < nodes.getLength(); i++) {
Node child = nodes.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) child;
if (!element.getTagName().equals("object")) {
throw new Error("expected tag 'object', found '" + element.getTagName() + "'");
}
CapturedObject root = processObject(element, context);
for (CapturedObject object : capturedObjects.values()) { // add all orphan objects to the root, so any non-inlineable ones have their XML created
if (object.getParent() == null && object != root) {
root.addChild(object, null);
}
}
return root.getXML(this);
}
}
return null;
} catch (SAXException e) {
throw new RuntimeException(e);
} finally {
reset();
}
}
private void reset() {
sourceObjects.clear();
capturedObjects.clear();
count = 0;
}
public static void main(String[] arg) throws Exception {
File file = new File(arg[0]);
JarFile jarFile = new JarFile(file);
ClassLoader classLoader = new URLClassLoader(new URL[]{file.toURI().toURL()});
Thread.currentThread().setContextClassLoader(classLoader);
EventQueue systemQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
systemQueue.push(new CaptureEventQueue(classLoader));
Manifest mf = jarFile.getManifest();
String mainClassName = mf.getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
Class> mainClass = Class.forName(mainClassName, true, classLoader);
Method main = mainClass.getMethod("main", String[].class);
main.invoke(null, new Object[]{new String[0]});
}
}