All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.jaxx.compiler.JAXXCompiler Maven / Gradle / Ivy

There is a newer version: 3.1.5
Show newest version
/*
 * #%L
 * JAXX :: Compiler
 * %%
 * Copyright (C) 2008 - 2020 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;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.eugene.GeneratorUtil;
import org.nuiton.eugene.java.JavaGeneratorUtil;
import org.nuiton.jaxx.compiler.binding.DataBindingHelper;
import org.nuiton.jaxx.compiler.css.StylesheetHelper;
import org.nuiton.jaxx.compiler.finalizers.I18nKeysFileModel;
import org.nuiton.jaxx.compiler.finalizers.JAXXCompilerFinalizer;
import org.nuiton.jaxx.compiler.java.JavaElementFactory;
import org.nuiton.jaxx.compiler.java.JavaField;
import org.nuiton.jaxx.compiler.java.JavaFile;
import org.nuiton.jaxx.compiler.java.JavaFileGenerator;
import org.nuiton.jaxx.compiler.java.JavaMethod;
import org.nuiton.jaxx.compiler.java.parser.ParseException;
import org.nuiton.jaxx.compiler.reflect.ClassDescriptor;
import org.nuiton.jaxx.compiler.reflect.ClassDescriptorHelper;
import org.nuiton.jaxx.compiler.reflect.FieldDescriptor;
import org.nuiton.jaxx.compiler.reflect.MethodDescriptor;
import org.nuiton.jaxx.compiler.reflect.resolvers.ClassDescriptorResolverFromJaxxFile;
import org.nuiton.jaxx.compiler.script.ScriptManager;
import org.nuiton.jaxx.compiler.tags.DefaultObjectHandler;
import org.nuiton.jaxx.compiler.tags.TagHandler;
import org.nuiton.jaxx.compiler.tags.TagManager;
import org.nuiton.jaxx.runtime.ComponentDescriptor;
import org.nuiton.jaxx.runtime.JAXXObject;
import org.nuiton.jaxx.runtime.JAXXObjectDescriptor;
import org.nuiton.jaxx.runtime.css.Rule;
import org.nuiton.jaxx.runtime.css.Stylesheet;
import org.nuiton.jaxx.runtime.i18n.I18nLabelsBuilder;
import org.nuiton.jaxx.runtime.spi.UIHandler;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.XMLFilterImpl;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXSource;
import java.awt.BorderLayout;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.Stack;

/**
 * Compiles a given {@link #jaxxFile} into a {@link #javaFile}.
 *
 * @author Tony Chemit - [email protected]
 * @since 1.0.0
 */
public class JAXXCompiler {

    public static final String JAXX_NAMESPACE =
            "http://www.jaxxframework.org/";
    public static final String JAXX_INTERNAL_NAMESPACE =
            "http://www.jaxxframework.org/internal";
    /** Maximum length of an inline creation method. */
    public static final int INLINE_THRESHOLD = 300;
    public static final String BORDER_LAYOUT_PREFIX = BorderLayout.class.getSimpleName() + ".";
    /** Logger */
    protected static final Logger log = LogManager.getLogger(JAXXCompiler.class);
    /** Line separator cached value */
    protected static final String lineSeparator =
            System.getProperty("line.separator", "\n");
    private static final String[] EMPTY_STRING_ARRAY = new String[0];
    /**
     * True to throw exceptions when we encounter unresolvable classes,
     * false to ignore.
     * 

* This is currently set to false until JAXX has full support for * inner classes (including enumerations), because currently they don't * always resolve (but will generally run without error anyway). */ public static boolean STRICT_CHECKS; /*------------------------------------------------------------------------*/ /*-- compiler fields -----------------------------------------------------*/ /*------------------------------------------------------------------------*/ /** The unique object handler used in first pass */ protected final DefaultObjectHandler firstPassClassTagHandler; /** Contains strings of the form "javax.swing." */ protected final Set importedPackages; /** Contains strings of the form "javax.swing.Timer" */ protected final Set importedClasses; /** Keeps track of open components (components still having children added). */ protected final Stack openComponents; /** to generate ids */ protected final IDHelper idHelper; /** Binding Util */ protected final DataBindingHelper bindingHelper; /** table of symbols for this compiler */ protected final SymbolTable symbolTable; /** * Base directory used for path resolution (normally the directory in * which the .jaxx file resides). */ protected final File baseDir; /** jaxx file being compiled. */ protected final File src; /** Name of class being compiled. */ protected final String outputClassName; /** script manager */ protected final ScriptManager scriptManager; /** Contains all attributes defined inline on class tags. */ protected final List inlineStyles; /** * Maps objects (expressed in Java code) to event listener classes * (e.g. MouseListener) to Lists of EventHandlers. The final list * contains all event handlers of a particular type attached to a * particular object (again, as represented by a Java expression). */ protected final Map>> eventHandlers; /** Map of event handler method names used in compiler */ protected final Map eventHandlerMethodNames; /** * A list of Runnable which will be run after the first compilation pass. *

* This is primarily used to trigger the creation of CompiledObjects, * which cannot be created during the first pass and must be created * in document order. */ protected final List initializers; /** Extra code to be added to the instance initializer. */ protected final StringBuilder initializer; /** Extra code to be added at the end of the instance initializer. */ protected final StringBuilder lateInitializer; /** Extra code to be added at the end of the instance initializer. */ protected final StringBuilder actionsInitializer; /** Extra code to be added to the class body. */ protected final StringBuilder bodyCode; /** Used for error reporting purposes, so we can report the right line number. */ protected final Stack tagsBeingCompiled; /** Used for error reporting purposes, so we can report the right source file. */ protected final Stack sourceFiles; /** * Maps object ID strings to the objects themselves. *

* These are created during the second compilation pass. */ protected final Map objects; /** * Maps objects to their ID strings. *

* These are created during the second compilation pass. */ protected final Map ids; /** * engine which references this compiler * (can be null if compiler is standalone) */ protected final JAXXEngine engine; protected final CompilerConfiguration defaultConfiguration = new DefaultCompilerConfiguration(); protected final JAXXCompilerFile jaxxFile; /** flag to detect if an error occurs while compiling jaxx file */ protected boolean failed; /** Object corresponding to the root tag in the document. */ protected CompiledObject root; /** Parsed XML of src file. */ protected Document document; /** * Combination of all stylesheets registered using * {@link #registerStylesheet(Stylesheet)}. */ protected Stylesheet stylesheet; /** * ClassLoader which searches the user-specified class path in * addition to the normal class path */ protected ClassLoader classLoader; /** true if a main() method has been declared in a script */ protected boolean mainDeclared; /** the file to be generated */ protected JavaFile javaFile; /** default decorator to use if none specified */ protected CompiledObjectDecorator defaultDecorator; /** * Flag to know if jaxx file ident css was found, otherwise add it * at the end of the second compile pass. * * @since 2.0.2 */ protected boolean identCssFound; /** * A flag to know if SwingUtil must be imported. * * @since 2.4 * @deprecated since 2.4.1, will be removed in version 3.0, it is not a good * idea to do special treatment for a particular class, to use SwingUtil, * do like for other class : import it! */ @Deprecated protected boolean needSwingUtil; protected String uiHandler; /** * extra interfaces which can by passed to root object via the * 'implements' attribute */ private String[] extraInterfaces; /** a flag to generate a abstract class */ private boolean abstractClass; /** the possible generic type of the class */ private String genericType; /** the possible generic type of the super class */ private String superGenericType; // private String i18nFormat; private I18nLabelsBuilder i18nLabelsBuilder; /** Keeps track of bean scopes. */ private final Stack beanScopeComponents; public JAXXCompiler() { this(null, null, null); } /*------------------------------------------------------------------------*/ /*-- Initializer methods -------------------------------------------------*/ /*------------------------------------------------------------------------*/ /** * Creates a new JAXXCompiler. * * @param engine engine which use the compiler (could be null if not attach to any engine) * @param jaxxFile the file to compile * @param defaultImports list of default imports to add to java files */ public JAXXCompiler(JAXXEngine engine, JAXXCompilerFile jaxxFile, List defaultImports) { this.engine = engine; this.jaxxFile = jaxxFile; ids = new LinkedHashMap<>(); objects = new LinkedHashMap<>(); bodyCode = new StringBuilder(); lateInitializer = new StringBuilder(); actionsInitializer = new StringBuilder(); initializer = new StringBuilder(); tagsBeingCompiled = new Stack<>(); initializers = new ArrayList<>(); eventHandlerMethodNames = new HashMap<>(); eventHandlers = new HashMap<>(); inlineStyles = new ArrayList<>(); scriptManager = new ScriptManager(this); symbolTable = new SymbolTable(); openComponents = new Stack<>(); beanScopeComponents = new Stack<>(); importedPackages = new HashSet<>(); importedClasses = new HashSet<>(); sourceFiles = new Stack<>(); if (jaxxFile == null) { src = null; baseDir = null; outputClassName = null; } else { src = jaxxFile.getJaxxFile(); baseDir = src.getParentFile(); outputClassName = jaxxFile.getClassName(); sourceFiles.push(src); addImport(outputClassName.substring( 0, outputClassName.lastIndexOf(".") + 1) + "*"); } firstPassClassTagHandler = new DefaultObjectHandler( ClassDescriptorHelper.getClassDescriptor(Object.class) ); bindingHelper = new DataBindingHelper(this); if (defaultImports != null) { for (String defaultImport : defaultImports) { addDefaultImport(defaultImport); } } if (engine != null) { CompilerConfiguration configuration = engine.getConfiguration(); // add extra imports from configuration if (configuration.getExtraImports() != null) { for (String extraImport : configuration.getExtraImports()) { addImport(extraImport); } } defaultDecorator = engine.getDecorator( configuration.getDefaultDecoratorClass()); if (defaultDecorator == null) { throw new IllegalArgumentException( "could not find default decorator : " + configuration.getDefaultDecoratorClass() ); } idHelper = new IDHelper(configuration.isOptimize()); } else { idHelper = new IDHelper(false); } } /** * Returns the system line separator string. * * @return the string used to separate lines */ public static String getLineSeparator() { return lineSeparator; } /*------------------------------------------------------------------------*/ /*-- Compile methods -----------------------------------------------------*/ /*------------------------------------------------------------------------*/ public static String getCanonicalName(Class clazz) { if (clazz.isArray()) { String canonicalName = getCanonicalName(clazz.getComponentType()); if (canonicalName != null) { return canonicalName + "[]"; } return null; } return clazz.getName().replace('$', '.'); } public static String getCanonicalName(ClassDescriptor clazz) { if (clazz.isArray()) { String canonicalName = getCanonicalName(clazz.getComponentType()); if (canonicalName != null) { return canonicalName + "[]"; } return null; } return clazz.getName().replace('$', '.'); } public static String getCanonicalName(CompiledObject compiled) { ClassDescriptor clazz = compiled.getObjectClass(); if (clazz.isArray()) { String canonicalName = getCanonicalName(clazz.getComponentType()); if (canonicalName != null) { if (compiled.getGenericTypesLength() > 0) { canonicalName += compiled.getGenericTypes(); } return canonicalName + "[]"; } return null; } String canonicalName = clazz.getName().replace('$', '.'); if (compiled.getGenericTypesLength() > 0) { canonicalName += compiled.getGenericTypes(); } return canonicalName; } /** * Escapes a string using standard Java escape sequences, generally in * preparation to including it in a string literal in a compiled Java file. * * @param raw the raw string to be escape * @return a string in which all 'dangerous' characters have been replaced * by equivalent Java escape sequences */ public static String escapeJavaString(String raw) { StringBuilder out = new StringBuilder(raw); for (int i = 0; i < out.length(); i++) { char c = out.charAt(i); if (c == '\\' || c == '"') { out.insert(i, '\\'); i++; } else if (c == '\n') { out.replace(i, i + 1, "\\n"); i++; } else if (c == '\r') { out.replace(i, i + 1, "\\r"); i++; } else if (c < 32 || c > 127) { String value = Integer.toString((int) c, 16); while (value.length() < 4) { value = "0" + value; } out.replace(i, i + 1, "\\u" + value); i += 5; } } return out.toString(); } /*------------------------------------------------------------------------*/ /*-- CompiledObject methods ----------------------------------------------*/ /*------------------------------------------------------------------------*/ public static File URLtoFile(URL url) { return URLtoFile(url.toString()); } public static File URLtoFile(String urlString) { if (!urlString.startsWith("file:")) { throw new IllegalArgumentException("url must start with 'file:'"); } urlString = urlString.substring("file:".length()); if (urlString.startsWith("/") && System.getProperty("os.name").startsWith("Windows")) { urlString = urlString.substring(1); } try { return new File(URLDecoder.decode( urlString.replace('/', File.separatorChar), "utf-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } public static SAXParser getSAXParser() { try { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); SAXParser parser; parser = factory.newSAXParser(); return parser; } catch (SAXException | ParserConfigurationException e) { throw new RuntimeException(e); } } public static Document parseDocument(InputStream in) throws IOException, SAXException { try { TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); transformer.setErrorListener(new ErrorListener() { @Override public void warning( TransformerException ex) throws TransformerException { throw ex; } @Override public void error( TransformerException ex) throws TransformerException { throw ex; } @Override public void fatalError( TransformerException ex) throws TransformerException { throw ex; } }); DOMResult result = new DOMResult(); transformer.transform( new SAXSource(new XMLFilterImpl(getSAXParser().getXMLReader()) { Locator locator; @Override public void setDocumentLocator(Locator locator) { this.locator = locator; } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { AttributesImpl resultAttributes = new AttributesImpl(attributes); resultAttributes.addAttribute( JAXX_INTERNAL_NAMESPACE, "line", "internal:line", "CDATA", String.valueOf(locator.getLineNumber()) ); getContentHandler().startElement(uri, localName, qName, resultAttributes ); } }, new InputSource(in)), result); return (Document) result.getNode(); } catch (TransformerConfigurationException e) { throw new RuntimeException(e); } catch (TransformerException e) { Throwable ex = e; while (ex.getCause() != null) { ex = ex.getCause(); } if (ex instanceof IOException) { throw (IOException) ex; } if (ex instanceof SAXException) { throw (SAXException) ex; } if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } throw new RuntimeException(ex); } } public void runInitializers() { for (Runnable runnable : initializers) { if (log.isDebugEnabled()) { log.debug(runnable); } try { runnable.run(); } catch (Exception e) { //TC - 20081018 report error and quit log.error("Can't run on initializer of file: " + jaxxFile, e); reportError(e.getMessage()); return; } } initializers.clear(); } /** * Registers a Runnable which will be executed after the first * compilation pass is complete. * * @param r runnable to register */ public void registerInitializer(Runnable r) { initializers.add(r); } public void compileFirstPass(final Element tag) throws IOException { tagsBeingCompiled.push(tag); String namespace = tag.getNamespaceURI(); String fullClassName; String localName = tag.getLocalName(); boolean namespacePrefix = tag.getPrefix() != null; // resolve class tags into fully-qualified class name if (namespace != null && namespace.endsWith("*")) { String packageName = namespace.substring(0, namespace.length() - 1); if (localName.startsWith(packageName)) { // class name is fully-qualified already fullClassName = TagManager.resolveClassName(localName, this); } else { // namespace not included in class name, probably need the // namespace to resolve fullClassName = TagManager.resolveClassName( packageName + localName, this); if (fullClassName == null && !namespacePrefix) { // it was just a default namespace, try again without // using the namespace fullClassName = TagManager.resolveClassName(localName, this); } } } else { fullClassName = TagManager.resolveClassName(localName, this); } if (fullClassName != null) { // we are definitely dealing with a class tag addDependencyClass(fullClassName); namespace = fullClassName.substring( 0, fullClassName.lastIndexOf(".") + 1) + "*"; if (symbolTable.getSuperclassName() == null) { symbolTable.setSuperclassName(fullClassName); } String id = tag.getAttribute(DefaultObjectHandler.ID_ATTRIBUTE); MethodDescriptor methodDescriptor; if (!id.isEmpty()) { symbolTable.getClassTagIds().put(id, fullClassName); if (tag.getAttributeNode(DefaultObjectHandler.JAVA_BEAN_ATTRIBUTE) != null) { // add java bean support for this property String capitalizeName = StringUtils.capitalize(id); // add method methodDescriptor = new MethodDescriptor( "get" + capitalizeName, Modifier.PUBLIC, fullClassName, EMPTY_STRING_ARRAY, getClassLoader() ); symbolTable.getScriptMethods().add(methodDescriptor); if (Boolean.class.getName().equals(fullClassName)) { methodDescriptor = new MethodDescriptor( "is" + capitalizeName, Modifier.PUBLIC, fullClassName, EMPTY_STRING_ARRAY, getClassLoader() ); symbolTable.getScriptMethods().add(methodDescriptor); } methodDescriptor = new MethodDescriptor( "set" + capitalizeName, Modifier.PUBLIC, JAXXCompilerFinalizer.TYPE_VOID, new String[]{fullClassName}, getClassLoader() ); symbolTable.getScriptMethods().add(methodDescriptor); } else { // add simple get support String capitalizeName = StringUtils.capitalize(id); // add method methodDescriptor = new MethodDescriptor( "get" + capitalizeName, Modifier.PUBLIC, fullClassName, EMPTY_STRING_ARRAY, getClassLoader() ); symbolTable.getScriptMethods().add(methodDescriptor); } } String interfacesStr = tag.getAttribute(DefaultObjectHandler.IMPLEMENTS_ATTRIBUTE); if (!interfacesStr.isEmpty()) { // there is some interfaces to deal with try { String[] interfaces = JavaGeneratorUtil.splitFqnList(interfacesStr, ','); if (log.isDebugEnabled()) { log.debug("detect interfaces : " + Arrays.toString(interfaces)); } symbolTable.setInterfaces(interfaces); } catch (Exception e) { // the syntax is not valid (missed one >) throw new CompilerException( "Syntax error of interfaces " + interfacesStr); } // StringTokenizer stk = new StringTokenizer(interfacesStr, ","); // List tmp = new ArrayList(); // while (stk.hasMoreTokens()) { // String c = stk.nextToken(); // if (c.contains("<") && !c.contains(">")) { // // deal with a generic interface with more than one // // parameter // boolean done = false; // while (stk.hasMoreTokens()) { // // String next = stk.nextToken(); // if (!next.contains(">")) { // // still a parameter of the generic type // continue; // } // // ok find the ending token // done = true; // break; // } // if (!done) { // // the syntax is not valid (missed one >) // throw new CompilerException( // "Syntax error of interfaces " + // interfacesStr); // } // //c = c.substring(0, c.indexOf("<")); // } // tmp.add(c.trim()); // } // String[] interfaces = tmp.toArray(new String[tmp.size()]); // if (log.isDebugEnabled()) { // log.debug("detect interfaces : " + // Arrays.toString(interfaces)); // } // symbolTable.setInterfaces(interfaces); } } // during the first pass, we can't create ClassDescriptors for JAXX // files because they may not have been processed yet (and we can't // wait until they have been processed because of circular // dependencies). // So we don't do any processing during the first pass which requires // having a ClassDescriptor; here we determine whether we have a class // tag or not (class tag namespaces end in "*") and use a generic // handler if so. // The real handler is used during the second pass. TagHandler handler = namespace != null && namespace.endsWith("*") ? firstPassClassTagHandler : TagManager.getTagHandler(tag.getNamespaceURI(), localName, namespacePrefix, this ); if (!firstPassClassTagHandler.equals(handler) && handler instanceof DefaultObjectHandler) { fullClassName = ((DefaultObjectHandler) handler).getBeanClass().getName(); handler = firstPassClassTagHandler; } if (firstPassClassTagHandler.equals(handler)) { final String finalClassName = fullClassName; // register an initializer which will create the // CompiledObject after pass 1 registerInitializer(() -> { DefaultObjectHandler handler1 = (DefaultObjectHandler) TagManager.getTagHandler( null, finalClassName, JAXXCompiler.this ); if (handler1 == null) { throw new CompilerException( "Internal error: missing TagHandler for '" + finalClassName + "'"); } handler1.registerCompiledObject(tag, JAXXCompiler.this); }); } if (handler == null) { reportError("Could not find a Java class corresponding to: <" + tag.getTagName() + ">"); failed = true; } else { try { handler.compileFirstPass(tag, this); } catch (CompilerException e) { reportError(e); } } Element finished = tagsBeingCompiled.pop(); if (finished != tag) { throw new IllegalStateException( "internal error: just finished compiling " + tag + ", but top of tagsBeingCompiled stack is " + finished); } } public void compileSecondPass(Element tag) throws IOException { tagsBeingCompiled.push(tag); TagHandler handler = TagManager.getTagHandler( tag.getNamespaceURI(), tag.getLocalName(), tag.getPrefix() != null, this ); if (handler == null) { reportError("Could not find a Java class corresponding to: <" + tag.getTagName() + ">"); failed = true; } else { handler.compileSecondPass(tag, this); } Element finished = tagsBeingCompiled.pop(); if (!tag.equals(finished)) { throw new RuntimeException( "internal error: just finished compiling " + tag + ", but top of tagsBeingCompiled stack is " + finished); } } public void compileFirstPass() throws IOException { try (InputStream in = new FileInputStream(src)) { document = parseDocument(in); Element documentElement = document.getDocumentElement(); boolean addAutoHandlerUI = getEngine().getConfiguration().isAddAutoHandlerUI(); String uiHandlerFullClassName; if (addAutoHandlerUI) { // try to find the uiHandlerFullClassName = getOutputClassName() + "Handler"; ClassDescriptor uiHandlerClass = null; try { uiHandlerClass = ClassDescriptorHelper.getClassDescriptor(uiHandlerFullClassName, getClassLoader()); if (uiHandlerClass != null && !ClassDescriptorHelper.isAssignableFrom(uiHandlerClass, UIHandler.class)) { String isAbstractValue = documentElement.getAttribute("abstract"); boolean isAbstract = "true".equals(isAbstractValue); if (!isAbstract) { reportWarning( "Found a handler " + uiHandlerFullClassName + " which does not implements " + UIHandler.class.getName()); } uiHandlerClass = null; } } catch (ClassNotFoundException e) { // not found } if (uiHandlerClass == null) { uiHandlerFullClassName = null; } } else { uiHandlerFullClassName = documentElement.getAttribute( DefaultObjectHandler.HANDLER_ATTRIBUTE); if (StringUtils.isNotBlank(uiHandlerFullClassName)) { // get his class descriptor ClassDescriptor uiHandlerClass = null; try { uiHandlerClass = ClassDescriptorHelper.getClassDescriptor(uiHandlerFullClassName, getClassLoader()); // check if implements UIHandler if (uiHandlerClass != null && !ClassDescriptorHelper.isAssignableFrom(uiHandlerClass, UIHandler.class)) { reportError( "Found a handler " + uiHandlerFullClassName + " which does not implements " + UIHandler.class.getName()); uiHandlerClass = null; } } catch (ClassNotFoundException e) { reportError("Could not find type " + uiHandlerFullClassName); } if (uiHandlerClass == null) { uiHandlerFullClassName = null; } } } if (uiHandlerFullClassName != null) { setUiHandler(uiHandlerFullClassName); } compileFirstPass(documentElement); } catch (SAXParseException e) { reportError(e.getLineNumber(), "Invalid XML: " + e.getMessage()); } catch (SAXException e) { reportError(null, "Error parsing XML document: " + e); } } /*------------------------------------------------------------------------*/ /*-- DataBinding methods -------------------------------------------------*/ /*------------------------------------------------------------------------*/ public void compileSecondPass() throws IOException { if (!tagsBeingCompiled.isEmpty()) { throw new RuntimeException( "Internal error: starting pass two, but tagsBeingCompiled" + " is not empty: " + tagsBeingCompiled); } compileSecondPass(document.getDocumentElement()); if (isUseHandler()) { String handler = getUIHandlerWithGeneric(); boolean isAbstract = isAbstractClass(); boolean addField = !isAbstract; boolean superclassIsJAXXObject; superclassIsJAXXObject = isSuperClassAware(JAXXObject.class); CompiledObject rootObject = getRootObject(); ClassDescriptor rootObjectClass = rootObject.getObjectClass(); if (superclassIsJAXXObject) { // test if there is a super handler try { rootObjectClass.getDeclaredFieldDescriptor("handler"); addField = false; } catch (NoSuchFieldException e) { // no field handler found } } JavaField field = JavaElementFactory.newField( Modifier.PROTECTED, handler, DefaultObjectHandler.HANDLER_ATTRIBUTE, !addField, null); if (addField) { javaFile.addField(field, false); } else if (getCompiledObject("handler") == null) { if (isAbstract) { String type = field.getType(); int indexOf = type.indexOf("<"); if (indexOf > -1) { type = type.substring(0, indexOf); } javaFile.addAbstractGetterMethod(field.getName(), Modifier.PUBLIC, type, superclassIsJAXXObject); } else { // override getter javaFile.addImport(field.getType()); javaFile.addOverrideGetterMethod(field.getName(), Modifier.PUBLIC, GeneratorUtil.getSimpleName(field.getType()), true); } } } // We could have a handler in a parent if so and if there is a createHandler method // then override it (to never use this code) ClassDescriptor objectClass = getRootObject().getObjectClass(); boolean foundMethod = false; while (!foundMethod && objectClass != null) { try { objectClass.getDeclaredMethodDescriptor("createHandler"); foundMethod = true; } catch (NoSuchMethodException e) { objectClass = objectClass.getSuperclass(); } } if (foundMethod) { JavaMethod createHandler = JavaElementFactory.newMethod( Modifier.PROTECTED, JAXXCompilerFinalizer.TYPE_VOID, "createHandler", "", true); javaFile.addMethod(createHandler); } } public void openComponent(CompiledObject component) throws CompilerException { openComponent(component, null); } public void openComponent(CompiledObject component, String constraints) throws CompilerException { if (constraints != null) { // try to add the constraints class in imports if (constraints.startsWith(BORDER_LAYOUT_PREFIX)) { addImport(BorderLayout.class.getName()); } } CompiledObject parent = getOpenComponent(); openInvisibleComponent(component); if (parent != null && (!component.isOverride() || component.isAddToContainer())) { parent.addChild(component, constraints, this); } component.getBeanScope().ifPresent(this::registerBeanScope); } /*------------------------------------------------------------------------*/ /*-- Script methods ------------------------------------------------------*/ /*------------------------------------------------------------------------*/ public void openInvisibleComponent(CompiledObject component) { if (!ids.containsKey(component)) { registerCompiledObject(component); } openComponents.push(component); } public CompiledObject getOpenComponent() { if (openComponents.isEmpty()) { return null; } return openComponents.peek(); } public void closeComponent(CompiledObject component) { if (component == null) { throw new NullPointerException("can not close a null component"); } if (!component.equals(openComponents.pop())) { throw new IllegalArgumentException( "can only close the topmost open object"); } if (component.getBeanScope().isPresent()) { removeBeanScope(); } } public void registerCompiledObject(CompiledObject object) { // assert engine.symbolTables.values().contains(symbolTable) : // "attempting to register CompiledObject before pass 1 is complete"; if (root == null) { root = object; } String id = object.getId(); if (ids.containsKey(object)) { reportError("object '" + object + "' is already registered with id '" + ids.get(object) + "', cannot re-register as '" + id + "'" ); } if (objects.containsKey(id) && !(objects.get(id) instanceof Element)) { reportError("id '" + id + "' is already registered to component " + objects.get(id)); } objects.put(id, object); ids.put(object, id); if (object.getDecorator() == null) { // use default decorator object.setDecorator(defaultDecorator); } } public CompiledObject getCompiledObject(String id) { runInitializers(); // assert engine.symbolTables.values().contains(symbolTable) : // "attempting to retrieve CompiledObject before pass 1 is complete"; return objects.get(id); } /*------------------------------------------------------------------------*/ /*-- StyleSheet methods --------------------------------------------------*/ /*------------------------------------------------------------------------*/ public boolean inlineCreation(CompiledObject object) { return object.getId().startsWith("$") && object.getInitializationCode(this).length() < INLINE_THRESHOLD; } public void checkOverride(CompiledObject object) throws CompilerException { String fieldName = object.getId(); if (fieldName.startsWith("$")) { return; } ClassDescriptor ancestor = root.getObjectClass(); if (object.getObjectClass().equals(ancestor)) { return; } while (ancestor != null) { try { FieldDescriptor f = ancestor.getDeclaredFieldDescriptor(fieldName); if (!f.getType().isAssignableFrom(object.getObjectClass())) { reportError( "attempting to redefine superclass member '" + fieldName + "' as incompatible type (was " + f.getType() + ", redefined as " + object.getObjectClass() + ")" ); } object.setOverride(true); object.setOverrideType(f.getType()); if (!getCanonicalName(f.getType()).equals(getCanonicalName(object))) { // types are not the same String simpleType = getImportedType(getCanonicalName(object)); if (log.isDebugEnabled()) { log.debug("Simple type for " + object.getId() + " : " + getCanonicalName(object) + " against : " + getCanonicalName(f.getType())); } object.setSimpleType(simpleType); } break; } catch (NoSuchFieldException e) { if (log.isDebugEnabled()) { log.debug(">>>>> could not find declared field [" + fieldName + "] in " + ancestor.getName()); } ancestor = ancestor.getSuperclass(); } } } // public void registerStyleSheetFile(File styleFile, boolean warnAutoCssImport) { // if (!identCssFound) { // // // detects if the given css file is ident to jaxx file // File identCssFile = jaxxFile.getCssFile(); // // if (styleFile.equals(identCssFile) && identCssFile.exists()) { // // ok found ident css file // identCssFound = true; // if (warnAutoCssImport) { // reportWarning("The css file " + styleFile + " can be automatically imported since it is named as his jaxx file."); // } // } // // } // String content = loadFile(styleFile); // getSourceFiles().push(styleFile); // try { // Stylesheet style = StylesheetHelper.processStylesheet(content); // registerStylesheet(style); // } catch (CompilerException e) { // String message = "Css file content is not valid :" + styleFile; // if (e instanceof org.nuiton.jaxx.compiler.css.parser.ParseException) { // org.nuiton.jaxx.compiler.css.parser.ParseException parseException = (org.nuiton.jaxx.compiler.css.parser.ParseException) e; // // message += " (line: " + parseException.getLine() + " - col:" + parseException.getColumn() + ") "; // } // reportError(message, e); // } finally { // // // whatever could be result, must pop this source file // getSourceFiles().pop(); // } // } public DataBindingHelper getBindingHelper() { return bindingHelper; } public void registerEventHandler(EventHandler handler) { String objectCode = handler.getObjectCode(); Map> listeners = eventHandlers.computeIfAbsent(objectCode, k -> new HashMap<>()); ClassDescriptor listenerClass = handler.getListenerClass(); List handlerList = listeners.computeIfAbsent(listenerClass, k -> new ArrayList<>()); handlerList.add(handler); } public String getEventHandlerMethodName(EventHandler handler) { String result = eventHandlerMethodNames.get(handler); if (result == null) { if (getConfiguration().isOptimize()) { result = "$ev" + eventHandlerMethodNames.size(); } else { //TC-20090309 must get the goal property from the event id // to make possible inheritance String id = handler.getEventId().substring( 0, handler.getEventId().indexOf(".")); result = "do" + StringUtils.capitalize(handler.getListenerMethod().getName()) + "__on__" + id; //TC-20091105 : check the method name is available // using css can have multi methods with result name (see priority) if (eventHandlerMethodNames.containsValue(result)) { int index = 0; String result2; do { result2 = result + "_" + index++; } while (eventHandlerMethodNames.containsValue(result2)); result = result2; } } eventHandlerMethodNames.put(handler, result); } return result; } /*------------------------------------------------------------------------*/ /*-- Report methods ------------------------------------------------------*/ /*------------------------------------------------------------------------*/ public void addScriptField(FieldDescriptor field) { symbolTable.getScriptFields().add(field); } public void addScriptMethod(MethodDescriptor method) { if (method.getName().equals("main") && method.getParameterTypes().length == 1 && method.getParameterTypes()[0].getName().equals("[Ljava.lang.String;")) { setMainDeclared(true); } symbolTable.getScriptMethods().add(method); } public void registerScript(String script) throws CompilerException { registerScript(script, null); } public void registerScript(String script, File sourceFile) throws CompilerException { if (sourceFile != null) { sourceFiles.push(sourceFile); } script = script.trim(); if (!"".equals(script) && !script.endsWith("}") && !script.endsWith(";")) { script += ";"; } scriptManager.registerScript(script); if (sourceFile != null) { File pop = sourceFiles.pop(); if (!sourceFile.equals(pop)) { throw new RuntimeException( "leaving registerScript(), but " + sourceFile + " was not the top entry on the stack (found " + pop + " instead)"); } } } public String preprocessScript(String script) throws CompilerException { return scriptManager.preprocessScript(script); } public boolean isIdentCssFound() { return identCssFound; } public void detectIdentStyleSheetFile() { // detects if the given css file is ident to jaxx file File identCssFile = jaxxFile.getCssFile(); if (identCssFile.exists()) { // ok found ident css file identCssFound = true; try { registerStyleSheetFile(identCssFile.toURI().toURL(), false); } catch (MalformedURLException e) { log.error(e); } } } static int commonCssDetectedCount =0; public void detectIdentCommonStyleSheetFile() { if (isSuperClassAware(JAXXObject.class)) { CompiledObject rootObject = getRootObject(); ClassDescriptor rootObjectClass = rootObject.getObjectClass(); String fileName = String.format("Common%s.jcss", rootObjectClass.getSimpleName()); String commonCssPath = String.format("%s/%s", rootObjectClass.getPackageName().replaceAll("\\.", "/"), fileName); URL resource = getClassLoader().getResource(commonCssPath); if (resource != null) { if (engine.isVerbose()) { log.info(String.format("Register Common css: %s [%d] on %s", fileName, ++commonCssDetectedCount, jaxxFile.getJaxxFile().getName())); } registerStyleSheetFile(resource, false); } } } public void registerStyleSheetFile(URL styleFile, boolean warnAutoCssImport) { if (warnAutoCssImport && identCssFound) { // detects if the given css file is ident to jaxx file File identCssFile = jaxxFile.getCssFile(); if (new File(styleFile.getFile()).toPath().equals(identCssFile.toPath()) && identCssFile.exists()) { // ok found ident css file reportWarning(String.format("The css file %s can be automatically imported since it is named as his jaxx file.", styleFile)); return; } } String content; try (InputStream inputStream = styleFile.openStream()) { content = IOUtils.toString(inputStream, StandardCharsets.UTF_8); } catch (IOException e) { reportError("Can't load common css: " + styleFile, new CompilerException(e)); return; } try { Stylesheet style = StylesheetHelper.processStylesheet(content); registerStylesheet(style); } catch (CompilerException e) { String message = "Css file content is not valid :" + styleFile; if (e instanceof org.nuiton.jaxx.compiler.css.parser.ParseException) { org.nuiton.jaxx.compiler.css.parser.ParseException parseException = (org.nuiton.jaxx.compiler.css.parser.ParseException) e; message += " (line: " + parseException.getLine() + " - col:" + parseException.getColumn() + ") "; } reportError(message, e); } } public void applyStylesheets() { for (Object o : new ArrayList<>(objects.values())) { CompiledObject object = (CompiledObject) o; DefaultObjectHandler tagHandler = TagManager.getTagHandler(object.getObjectClass()); if (tagHandler==null) { continue; } log.debug(String.format("will apply css on object %s from handler %s", object.getId(), tagHandler)); tagHandler.applyStylesheets(object, this); } } /*------------------------------------------------------------------------*/ /*-- Getter methods ------------------------------------------------------*/ /*------------------------------------------------------------------------*/ public void registerStylesheet(Stylesheet stylesheet) { if (this.stylesheet == null) { this.stylesheet = stylesheet; } else { this.stylesheet.add(stylesheet.getRules()); } } public void addInlineStyle(CompiledObject object, String propertyName, boolean dataBinding) { Rule style = StylesheetHelper.inlineAttribute( object, propertyName, dataBinding ); inlineStyles.add(style); } public void reportWarning(String warning) { Element currentTag = null; if (!tagsBeingCompiled.isEmpty()) { currentTag = tagsBeingCompiled.peek(); } reportWarning(currentTag, warning, 0); } public void reportWarning(Element tag, String warning, int lineOffset) { String lineNumber = null; if (tag != null) { String lineAttr = tag.getAttributeNS(JAXX_INTERNAL_NAMESPACE, "line"); if (lineAttr.length() > 0) { lineNumber = lineAttr; } } File srcFile = sourceFiles.peek(); try { srcFile = srcFile.getCanonicalFile(); } catch (IOException e) { // ignore ? } StringBuilder buffer = new StringBuilder(); buffer.append(srcFile); if (lineNumber != null) { buffer.append(":"); buffer.append(sourceFiles.size() == 1 ? Integer.parseInt(lineNumber) + lineOffset : lineOffset + 1 ); } buffer.append(getLineSeparator()).append(warning.trim()); if (engine != null) { engine.addWarning(buffer.toString()); } else { System.err.println(buffer.toString()); } } public void reportError(String error) { Element currentTag = null; if (!tagsBeingCompiled.isEmpty()) { currentTag = tagsBeingCompiled.peek(); } reportError(currentTag, error); } public void reportError(CompilerException ex) { reportError(null, ex); } public void reportError(String extraMessage, CompilerException ex) { String message = ex.getMessage(); if (UnsupportedAttributeException.class.equals(ex.getClass()) || UnsupportedTagException.class.equals(ex.getClass())) { String exceptionName = ex.getClass().getName(); message = exceptionName.substring( exceptionName.lastIndexOf(".") + 1) + ": " + message; } int lineOffset; if (ex instanceof ParseException) { lineOffset = Math.max(0, ((ParseException) ex).getLine() - 1); } else { lineOffset = 0; } Element currentTag = null; if (!tagsBeingCompiled.isEmpty()) { currentTag = tagsBeingCompiled.peek(); } reportError(currentTag, extraMessage != null ? extraMessage + message : message, lineOffset ); } public void reportError(Element tag, String error) { reportError(tag, error, 0); } public void reportError(Element tag, String error, int lineOffset) { int lineNumber = 0; if (tag != null) { String lineAttr = tag.getAttributeNS(JAXX_INTERNAL_NAMESPACE, "line"); if (lineAttr.length() > 0) { lineNumber = Integer.parseInt(lineAttr); } } lineNumber = Math.max(lineNumber, 1) + lineOffset; reportError(lineNumber, error); } public void reportError(int lineNumber, String error) { File errorFile = sourceFiles.isEmpty() ? null : sourceFiles.peek(); try { if (errorFile != null) { errorFile = errorFile.getCanonicalFile(); } } catch (IOException e) { // ignore ? } StringBuilder buffer = new StringBuilder(); buffer.append(errorFile != null ? errorFile.getPath() : ""); if (lineNumber > 0) { buffer.append(":").append(lineNumber); } buffer.append(getLineSeparator()).append(": ").append(error); if (engine != null) { engine.addError(buffer.toString()); } else { System.err.println(buffer.toString()); } failed = true; } public Map getObjects() { return objects; } public Map>> getEventHandlers() { return eventHandlers; } public CompilerConfiguration getConfiguration() { JAXXEngine engine = getEngine(); if (engine == null) { return defaultConfiguration; } return engine.getConfiguration(); } public String getOutputClassName() { return outputClassName; } public File getBaseDir() { return baseDir; } public Set getImportedClasses() { return importedClasses; } public Set getImportedPackages() { return importedPackages; } public Iterator getObjectCreationOrder() { return objects.values().iterator(); } public CompiledObject getRootObject() { return root; } /*------------------------------------------------------------------------*/ /*-- Buffer --------------------------------------------------------------*/ /*------------------------------------------------------------------------*/ public Stack getSourceFiles() { return sourceFiles; } public ScriptManager getScriptManager() { return scriptManager; } public SymbolTable getSymbolTable() { return symbolTable; } public Stylesheet getStylesheet() { Stylesheet merged = new Stylesheet(); if (stylesheet != null) { merged.add(stylesheet.getRules()); } merged.add(inlineStyles.toArray(new Rule[0])); return merged; } public FieldDescriptor getScriptField(String fieldName) { for (FieldDescriptor f : symbolTable.getScriptFields()) { if (fieldName.equals(f.getName())) { return f; } } return null; } public MethodDescriptor getScriptMethod(String methodName) { for (MethodDescriptor m : symbolTable.getScriptMethods()) { if (methodName.equals(m.getName())) { return m; } } return null; } public boolean isFailed() { return failed; } /** * Returns a ClassLoader which searches the user-specified * class path in addition to the normal system class path. * * @return ClassLoader to use while resolving class references */ public ClassLoader getClassLoader() { if (classLoader == null) { CompilerConfiguration configuration = getConfiguration(); if (configuration.getClassLoader() != null) { classLoader = configuration.getClassLoader(); } else { throw new NullPointerException( "compiler configuration requires a classLoader! :\n" + configuration); } } return classLoader; } /*------------------------------------------------------------------------*/ /*-- Other methods -------------------------------------------------------*/ /*------------------------------------------------------------------------*/ public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } /** * Checks if the super class of the mirrored javaFile is aware of the * given {@code type}. *

* Note: If no super-class exist, then returns * {@code false}. * * @param type the type to check against super class * @return {@code true} if super class exists and is assignable against the * given type, {@code false} otherwise */ public boolean isSuperClassAware(Class type) { ClassDescriptor superClass = root.getObjectClass(); if (superClass == null) { // no super class, not aware of anything return false; } return ClassDescriptorHelper.isAssignableFrom(superClass, type); } public JAXXObjectDescriptor getJAXXObjectDescriptor() { runInitializers(); CompiledObject[] components = new ArrayList<>( objects.values()).toArray(new CompiledObject[objects.size()]); assert initializers.isEmpty() : "there are pending initializers remaining"; assert root != null : "root object has not been defined"; assert Arrays.asList(components).contains(root) : "root object is not registered"; ComponentDescriptor[] descriptors = new ComponentDescriptor[components.length]; // as we print, sort the array so that component's parents are always // before the components themselves for (int i = 0; i < components.length; i++) { CompiledObject parent = components[i].getParent(); while (parent != null) { boolean found = false; for (int j = i + 1; j < components.length; j++) { // found parent after component, swap them if (components[j] == parent) { components[j] = components[i]; components[i] = parent; found = true; break; } } if (!found) { break; } parent = components[i].getParent(); } int parentIndex = -1; if (parent != null) { for (int j = 0; j < i; j++) { if (components[j].equals(parent)) { parentIndex = j; break; } } } descriptors[i] = new ComponentDescriptor( components[i].getId(), components[i] == root ? outputClassName : components[i].getObjectClass().getName(), components[i].getStyleClass(), parentIndex != -1 ? descriptors[parentIndex] : null); } Stylesheet css = getStylesheet(); if (css == null) { css = new Stylesheet(); } return new JAXXObjectDescriptor(descriptors, css); } public StringBuilder getInitializer() { return initializer; } public StringBuilder getLateInitializer() { return lateInitializer; } public StringBuilder getActionsInitializer() { return actionsInitializer; } public StringBuilder getBodyCode() { return bodyCode; } public boolean isMainDeclared() { return mainDeclared; } public void setMainDeclared(boolean mainDeclared) { this.mainDeclared = mainDeclared; } public void appendInitializerCode(String code) { initializer.append(code); } public void appendBodyCode(String code) { bodyCode.append(code); } public void appendLateInitializer(String code) { lateInitializer.append(code); } public void appendActionInitializer(String code) { actionsInitializer.append(code); } public void addImport(Class clazz) { addImport(clazz.getName()); } public void addImport(String text) { if (text.endsWith("*")) { importedPackages.add(text.substring(0, text.length() - 1)); } else { importedClasses.add(text); } if (!text.equals("*")) { getJavaFile().addImport(text); } } protected void addDefaultImport(String text) { if (text.endsWith("*")) { importedPackages.add(text.substring(0, text.length() - 1)); } else { importedClasses.add(text); } } public void addDependencyClass(String className) { if (engine.containsJaxxFileClassName(className)) { // already registered in engine return; } // register the jaxx file into the engine URL jaxxURL = ClassDescriptorHelper.getURL(getClassLoader(), className, "jaxx"); if (jaxxURL == null) { // not a jaxx file, can quit now return; } if (!engine.isCompileFirstPassTask()) { throw new IllegalStateException( "Internal error: adding dependency class " + className + " during second compilation pass"); } //FIXME tchemit 20100518 Must unify this with ClassDescriptorHelper to use the less costless resource if (jaxxURL.toString().startsWith("file:")) { File jaxxFile = URLtoFile(jaxxURL); try { jaxxFile = jaxxFile.getCanonicalFile(); } catch (IOException ex) { // ignore ? (at least log it...) log.error(ex); } String jaxxFileName = className.substring(className.lastIndexOf(".") + 1) + ".jaxx"; assert jaxxFile.getName().equalsIgnoreCase(jaxxFileName) : "expecting file name to match " + className + ", but found " + jaxxFile.getName(); if (jaxxFile.getName().equals(jaxxFileName)) { engine.addFileToCompile(jaxxFile, className); } } } /** * Loads the given file and returns its content. *

* Note: If any errors encounters, then report an error in the * compiler and returns an empty string. * * @param file the file to load * @return the content of the file or empty string if something was wrong */ public String loadFile(File file) { if (!file.exists()) { reportError("Could not found file " + file); return ""; } try { try (FileReader in = new FileReader(file)) { StringWriter styleBuffer = new StringWriter(); char[] readBuffer = new char[2048]; int c; while ((c = in.read(readBuffer)) > 0) { styleBuffer.write(readBuffer, 0, c); } return styleBuffer.toString(); } } catch (IOException e) { reportError("Could not read file " + file + " for reason " + e.getMessage()); return ""; } } /** * Verifies that a snippet of Java code parses correctly. *

* A warning is generated if the string has enclosing curly braces. * * @param javaCode the Java code snippet to test * @return a "cooked" version of the string which has enclosing curly * braces removed. * @throws CompilerException if the code cannot be parsed */ public String checkJavaCode(String javaCode) { return checkJavaCode(javaCode, true); } /** * Verifies that a snippet of Java code parses correctly. *

* A warning is generated if the string has enclosing curly braces. * * @param javaCode the Java code snippet to test * @param warnOnBrace flag to warn if curly braces are found * @return a "cooked" version of the string which has enclosing curly * braces removed. * @throws CompilerException if the code cannot be parsed */ public String checkJavaCode(String javaCode, boolean warnOnBrace) { javaCode = scriptManager.trimScript(javaCode, warnOnBrace); scriptManager.checkParse(javaCode); return javaCode; } /** * Check that a reference exists in symbol table on second compil pass * * @param tag the current tag * @param reference the required reference * @param strict flag to report an error if reference was not found * @param attribute (if not null reference the attribute where is defined * the reference) * @return true if reference was found, false * otherwise and add an error in compiler */ public boolean checkReference(Element tag, String reference, boolean strict, String attribute) { String component = getSymbolTable().getClassTagIds().get(reference); if (component == null) { try { if (isSuperClassAware(JAXXObject.class)) { ClassDescriptor objectClass = getRootObject().getObjectClass(); if (objectClass instanceof ClassDescriptorResolverFromJaxxFile.JaxxFileClassDescriptor) { ClassDescriptorResolverFromJaxxFile.JaxxFileClassDescriptor objectClass1 = (ClassDescriptorResolverFromJaxxFile.JaxxFileClassDescriptor) objectClass; component = objectClass1.getDeclaredFieldDescriptor(reference).getName(); } else { Optional fieldDescriptor = objectClass.tryToGetFieldDescriptor(reference); if (fieldDescriptor.isPresent()) { component = fieldDescriptor.get().getName(); } } } } catch (NoSuchFieldException e) { // can't happen } } if (component == null) { if (strict) { String msg; if (attribute != null) { msg = "tag '" + tag.getLocalName() + "' could not find the reference '" + reference + "' on attribute [" + attribute + "]"; } else { msg = "tag '" + tag.getLocalName() + "' could not find the reference '" + reference + "'"; } reportError(msg); } return false; } return true; } public String getAutoId(String name) { return idHelper.nextId(name); } public String getUniqueId(Object object) { return idHelper.getUniqueId(object); } public String[] getExtraInterfaces() { return extraInterfaces; } public void setExtraInterfaces(String[] extraInterfaces) { this.extraInterfaces = extraInterfaces; } // 1.5 adds getCanonicalName; unfortunately we can't depend on 1.5 features yet public boolean isAbstractClass() { return abstractClass; } public void setAbstractClass(boolean abstractClass) { this.abstractClass = abstractClass; } public String getGenericType() { return genericType; } public void setGenericType(String genericType) { this.genericType = genericType; } public String getSuperGenericType() { return superGenericType; } public void setSuperGenericType(String superGenericType) { this.superGenericType = superGenericType; } public void addSimpleField(JavaField javaField) { getJavaFile().addSimpleField(javaField); } public JavaFile getJavaFile() { if (javaFile == null) { String outputClassName = getOutputClassName(); if (outputClassName == null) { javaFile = JavaElementFactory.newFile(0, ""); } else { int dotPos = outputClassName.lastIndexOf("."); String packageName = dotPos != -1 ? outputClassName.substring(0, dotPos) : null; String simpleClassName = outputClassName.substring(dotPos + 1); javaFile = JavaElementFactory.newFile(Modifier.PUBLIC, packageName, simpleClassName); } } return javaFile; } public void finalizeCompiler() throws Exception { int dotPos = getOutputClassName().lastIndexOf("."); String packageName = dotPos != -1 ? getOutputClassName().substring(0, dotPos) : null; String simpleClassName = getOutputClassName().substring(dotPos + 1); CompiledObject compiledObject = getRootObject(); String genericType = getGenericType(); if (StringUtils.isNotEmpty(genericType)) { // add the generic type to the root object // generic type can be on form E extends XXX //keep only the first thing... StringBuilder sb = new StringBuilder(); String[] allTypes = JavaGeneratorUtil.splitFqnList(genericType, ','); for (String type : allTypes) { int anExtends = type.indexOf("extends"); if (anExtends > -1) { type = type.substring(0, anExtends - 1).trim(); } sb.append(", ").append(type); } String finalType; if (allTypes.length > 0) { finalType = sb.substring(2); } else { finalType = sb.toString(); } compiledObject.setGenericTypes(finalType); } // finalize all objects via their decorator for (CompiledObject object : getObjects().values()) { CompiledObjectDecorator decorator = object.getDecorator(); decorator.finalizeCompiler(this, root, object, javaFile, packageName, simpleClassName, getOutputClassName()); } // obtain list of finalizers to apply List realFinalizers = new ArrayList<>(); for (JAXXCompilerFinalizer finalizer : getConfiguration().getFinalizers().values()) { if (finalizer.accept(this)) { realFinalizers.add(finalizer); } } // call the finalizers finalizeCompiler method for (JAXXCompilerFinalizer finalizer : realFinalizers) { // check if finalizer can be apply of this compiler if (finalizer.accept(this)) { finalizer.finalizeCompiler(compiledObject, this, javaFile, packageName, simpleClassName ); } } // call the compiled objects finalizeCompiler method for (CompiledObject object : getObjects().values()) { object.finalizeCompiler(this); } // compile bindings getBindingHelper().finalizeBindings(); // call the finalizers prepareJavaFile method for (JAXXCompilerFinalizer finalizer : realFinalizers) { finalizer.prepareJavaFile(compiledObject, this, javaFile, packageName, simpleClassName ); } } public void generate(JavaFileGenerator generator) throws IOException { File dest; String fqn = getOutputClassName(); if (getConfiguration().getTargetDirectory() != null) { dest = new File(getConfiguration().getTargetDirectory(), fqn.replace('.', File.separatorChar) + ".java" ); } else { dest = new File(getBaseDir(), fqn.substring(fqn.lastIndexOf(".") + 1) + ".java" ); } // make sure directory exists File parentFile = dest.getParentFile(); if (parentFile == null) { throw new IOException("No parent file for " + dest); } if (!parentFile.exists() && !parentFile.mkdirs()) { throw new IOException("Could not create directory " + parentFile); } if (dest.exists() && !dest.setLastModified(System.currentTimeMillis())) { log.warn("could not touch file " + dest); } try (PrintWriter out = new PrintWriter(new FileWriter(dest))) { generator.generateFile(javaFile, out); } } public JAXXEngine getEngine() { return engine; } public void clear() { idHelper.clear(); bindingHelper.clear(); objects.clear(); ids.clear(); if (symbolTable != null) { symbolTable.clear(); } } /** * Try to import the given type into the underlying java file of this compiler. *

* If import can not be done, will then returns the fully qualified name of * the type. * * @param type the type to simplify * @return the simplify type or the fqn if type could not be imported for the underlying java file. * @since 2.4.1 */ public String getImportedType(Class type) { return getJavaFile().getImportedType(type); } /** * Try to import the given type into the underlying java file of this compiler. *

* If import can not be done, will then returns the fully qualified name of * the type. * * @param type the fqn of the type to simplify * @return the simplify type or the fqn if type could not be imported for the underlying java file. * @since 2.4.1 */ public String getImportedType(String type) { return getJavaFile().getImportedType(type); } public String getImportedTypeForSimpleName(String type) { String suffix = "." + type; String result = null; for (String importedClass : getImportedClasses()) { if (importedClass.endsWith(suffix)) { result = importedClass; break; } } return result; } public String getUiHandler() { return uiHandler; } public void setUiHandler(String uiHandler) { this.uiHandler = uiHandler; } public String getUIHandlerWithGeneric() { String genericType = getGenericType(); String result = uiHandler; if (genericType != null) { String currentGenericType = genericType; int beginIndex = currentGenericType.indexOf(" extends"); boolean first = false; if (beginIndex > -1) { result += "<"; while (beginIndex > -1) { genericType = currentGenericType.substring(0, beginIndex).trim(); if (!first) { first = true; } else { int lastIndexOf = genericType.lastIndexOf(","); genericType = genericType.substring(lastIndexOf).trim(); } result += genericType; currentGenericType = currentGenericType.substring(beginIndex + 8).trim(); beginIndex = currentGenericType.indexOf(" extends"); } result += ">"; } else { result += "<" + genericType + ">"; } } return result; } public boolean isUseHandler() { return StringUtils.isNotBlank(uiHandler); } // public void setI18nFormat(String i18nFormat) { // this.i18nFormat = i18nFormat; // } // public String computeI18n(String editorName, String value) { // String format = StringUtils.isEmpty(value) ? i18nFormat : value; // if (StringUtils.isEmpty(format)) { // throw new IllegalStateException("Can't use computeI18n method if i18nFormat missing on root object"); // } // return String.format(format, StringUtils.removeEnd(editorName, "Label")); // } public String computeI18nProperty(CompiledObject editorName) { return computeI18nProperty(editorName, null); } public String computeI18nProperty(CompiledObject editorName, String suffix) { Objects.requireNonNull(i18nLabelsBuilder); return i18nLabelsBuilder.getI18nKey(StringUtils.removeEnd(editorName.getI18nProperty(), "Label")+(suffix==null?"":suffix)); } public String computeI18nProperty(String editorName) { Objects.requireNonNull(i18nLabelsBuilder); return i18nLabelsBuilder.getI18nKey(editorName); } public String getI18nProperty(CompiledObject compiledObject) { return getEngine().getI18nProperty(compiledObject.getObjectClass()); } public String processDataBindings(String stringValue) throws CompilerException { return bindingHelper.processDataBindings(stringValue); } public boolean isI18nInit() { return i18nLabelsBuilder != null; } public void setI18n(String i18n) { try { this.i18nLabelsBuilder = new I18nLabelsBuilder(getClassLoader().loadClass(Objects.requireNonNull(i18n))); } catch (ClassNotFoundException e) { throw new IllegalStateException("can't get class: "+i18n); } } public Optional getBeanScope() { return Optional.ofNullable(beanScopeComponents.isEmpty()?null:beanScopeComponents.peek()); } public void registerBeanScope(BeanScope beanScope) { log.debug("add: " + beanScope); beanScopeComponents.push(beanScope); } public void removeBeanScope() { BeanScope beanScope = beanScopeComponents.pop(); log.debug("remove: " + beanScope); } public I18nKeysFileModel getParentI18nKeysFileModel(boolean load) { return getEngine().getI18nKeysFileModel(getRootObject().getObjectClass().getName(), load); } public I18nKeysFileModel getI18nKeysFileModel(boolean load) { return getEngine().getI18nKeysFileModel(getOutputClassName(), load); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy