org.floggy.synchronization.jme.weaver.Weaver.java Source code

Java tutorial

Introduction

Here is the source code for org.floggy.synchronization.jme.weaver.Weaver.java

Source

/**
 * Copyright (c) 2006-2010 Floggy Open Source Group. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.floggy.synchronization.jme.weaver;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;

import java.net.URL;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.Modifier;
import javassist.NotFoundException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.floggy.synchronization.jme.core.impl.SynchronizableMetadata;
import org.floggy.synchronization.jme.core.impl.SynchronizableMetadataManager;
import org.floggy.synchronization.jme.weaver.codegen.json.CodeGenerator;

import net.sourceforge.floggy.persistence.pool.InputPool;
import net.sourceforge.floggy.persistence.pool.OutputPool;
import net.sourceforge.floggy.persistence.pool.PoolFactory;

/**
* DOCUMENT ME!
*
* @author <a href="mailto:thiago.moreira@floggy.org">Thiago Moreira</a>
* @version $Revision$
 */
public class Weaver {
    private static final Log LOG = LogFactory.getLog(Weaver.class);
    public static final String __SYNCHRONIZABLE_CLASSNAME = "org.floggy.synchronization.jme.core.impl.__Synchronizable";
    public static final String SYNCHRONIZABLE_CLASSNAME = "org.floggy.synchronization.jme.core.Synchronizable";

    /** DOCUMENT ME! */
    protected ClassPool classpathPool;

    /** DOCUMENT ME! */
    protected Configuration configuration = new Configuration();

    /** DOCUMENT ME! */
    protected File configurationFile;

    /** DOCUMENT ME! */
    protected InputPool inputPool;

    /** DOCUMENT ME! */
    protected OutputPool embeddedClassesOutputPool;

    /** DOCUMENT ME! */
    protected OutputPool outputPool;

    /** DOCUMENT ME! */
    protected Set alreadyProcessedMetadatas = new HashSet();

    /**
       * Creates a new instance
       * 
       * @param args
       */
    public Weaver() {
        this(new ClassPool());
    }

    /**
       * Creates a new instance
       * 
       * @param args
       */
    public Weaver(ClassPool classPool) {
        this.classpathPool = classPool;
    }

    /**
    * DOCUMENT ME!
    *
    * @param ctClass DOCUMENT ME!
    *
    * @return DOCUMENT ME!
    *
    * @throws NotFoundException DOCUMENT ME!
    */
    public SynchronizableMetadata createSynchronizableMetadata(CtClass ctClass) throws NotFoundException {
        String className = ctClass.getName();

        CtField[] fields = ctClass.getFields();

        List fieldNames = new ArrayList(fields.length);
        List fieldTypes = new ArrayList(fields.length);

        for (int i = 0; i < fields.length; i++) {
            CtField field = (CtField) fields[i];

            if (ignoreField(field)) {
                continue;
            }

            fieldNames.add(field.getName());

            Integer type = buildFloggyFieldType(field.getType());
            fieldTypes.add(type);
        }

        int[] temp = new int[fieldTypes.size()];

        for (int i = 0; i < fieldTypes.size(); i++) {
            temp[i] = ((Integer) fieldTypes.get(i)).intValue();
        }

        SynchronizableMetadata metadata = new SynchronizableMetadata(className,
                (String[]) fieldNames.toArray(new String[fieldNames.size()]), temp);

        return metadata;
    }

    /**
    * DOCUMENT ME!
    *
    * @throws WeaverException DOCUMENT ME!
    */
    public void execute() throws WeaverException {
        long time = System.currentTimeMillis();
        LOG.info("Floggy Synchronization JME Weaver - " + SynchronizableMetadataManager.CURRENT_VERSION);
        LOG.info("CLDC version: " + ((isCLDC10()) ? "1.0" : "1.1"));

        try {
            URL fileURL = getClass()
                    .getResource("/org/floggy/synchronization/jme/core/impl/SynchronizableMetadataManager.class");
            classpathPool.makeClass(fileURL.openStream());

            embeddedUnderlineCoreClasses();

            List list = getClassThatImplementsSynchronizable();

            int classCount = list.size();
            LOG.info("Processing " + classCount + " bytecodes!");

            for (int i = 0; i < classCount; i++) {
                String className = (String) list.get(i);

                CtClass ctClass = this.classpathPool.get(className);

                LOG.info("Processing bytecode " + className + "!");

                CodeGenerator codeGenerator = new CodeGenerator(ctClass, classpathPool, configuration);

                codeGenerator.generateCode();

                if (configuration.isGenerateSource()) {
                    byte[] source = codeGenerator.getSource().getBytes();
                    String fileName = className.replace('.', File.separatorChar) + ".txt";
                    outputPool.addResource(new ByteArrayInputStream(source), fileName);
                }

                this.outputPool.addClass(ctClass);

                if (LOG.isDebugEnabled())
                    LOG.debug("Bytecode modified.");
            }

            addSynchronizableMetadataManagerClass();

            if (embeddedClassesOutputPool != outputPool) {
                embeddedClassesOutputPool.finish();
            }

            outputPool.finish();
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            throw new WeaverException(e.getMessage(), e);
        }

        time = System.currentTimeMillis() - time;
        LOG.info("Time elapsed: " + time + "ms");
    }

    /**
    * Sets the classpath.
    *
    * @param classpath DOCUMENT ME!
    */
    public void setClasspath(String[] classpath) {
        if ((classpath != null) && (classpath.length > 0)) {
            for (int i = classpath.length - 1; i >= 0; i--) {
                try {
                    this.classpathPool.insertClassPath(classpath[i]);
                } catch (NotFoundException e) {
                }
            }
        }
    }

    /**
    * DOCUMENT ME!
    *
    * @param configuration DOCUMENT ME!
    */
    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;
    }

    /**
    * DOCUMENT ME!
    *
    * @param configurationFile DOCUMENT ME!
    */
    public void setConfigurationFile(File configurationFile) {
        this.configurationFile = configurationFile;
    }

    /**
    * DOCUMENT ME!
    *
    * @param embeddedClassesOutputFile DOCUMENT ME!
    */
    public void setEmbeddedClassesOutputPool(File embeddedClassesOutputFile)
            throws net.sourceforge.floggy.persistence.WeaverException {
        embeddedClassesOutputPool = PoolFactory.createOutputPool(embeddedClassesOutputFile);
    }

    /**
    * Sets the input file.
    *
    * @param inputFile DOCUMENT ME!
    */
    public void setInputFile(File inputFile) throws net.sourceforge.floggy.persistence.WeaverException {
        this.inputPool = PoolFactory.createInputPool(inputFile);

        try {
            this.classpathPool.insertClassPath(inputFile.getCanonicalPath());
        } catch (NotFoundException e) {
        } catch (IOException e) {
        }
    }

    /**
    * Sets the output file.
    *
    * @param outputFile
    */
    public void setOutputFile(File outputFile) throws net.sourceforge.floggy.persistence.WeaverException {
        outputPool = PoolFactory.createOutputPool(outputFile);

        if (embeddedClassesOutputPool == null) {
            embeddedClassesOutputPool = outputPool;
        }
    }

    /**
    * DOCUMENT ME!
    *
    * @throws CannotCompileException DOCUMENT ME!
    * @throws IOException DOCUMENT ME!
    * @throws NotFoundException DOCUMENT ME!
    */
    protected void addSynchronizableMetadataManagerClass()
            throws CannotCompileException, IOException, NotFoundException {
        alreadyProcessedMetadatas.addAll(configuration.getSynchronizableMetadatas());

        Set metadatas = alreadyProcessedMetadatas;
        StringBuffer buffer = new StringBuffer();

        buffer.append("public static void init() throws Exception {\n");
        buffer.append("synchronizableMetadatas = new java.util.Hashtable();\n");

        for (Iterator iterator = metadatas.iterator(); iterator.hasNext();) {
            SynchronizableMetadata metadata = (SynchronizableMetadata) iterator.next();
            String className = metadata.getClassName();
            String[] fieldNames = metadata.getFieldNames();
            int[] fieldTypes = metadata.getFieldTypes();

            StringBuffer fieldNamesBuffer = new StringBuffer("new String[");
            StringBuffer fieldTypesBuffer = new StringBuffer("new int[");
            boolean addHeader = true;

            for (int i = 0; i < fieldNames.length; i++) {
                if (addHeader) {
                    fieldNamesBuffer.append("]{");
                    fieldTypesBuffer.append("]{");
                    addHeader = false;
                }

                fieldNamesBuffer.append("\"");
                fieldNamesBuffer.append(fieldNames[i]);
                fieldNamesBuffer.append("\",");

                fieldTypesBuffer.append(fieldTypes[i]);
                fieldTypesBuffer.append(",");
            }

            if (addHeader) {
                fieldNamesBuffer.append("0]");
                fieldTypesBuffer.append("0]");
            } else {
                fieldNamesBuffer.deleteCharAt(fieldNamesBuffer.length() - 1);
                fieldNamesBuffer.append("}");
                fieldTypesBuffer.deleteCharAt(fieldTypesBuffer.length() - 1);
                fieldTypesBuffer.append("}");
            }

            buffer.append("synchronizableMetadatas.put(\"" + className
                    + "\", new org.floggy.synchronization.jme.core.impl.SynchronizableMetadata(\"" + className
                    + "\", " + fieldNamesBuffer.toString() + ", " + fieldTypesBuffer.toString() + "));\n");
        }

        buffer.append("}\n");

        CtClass ctClass = this.classpathPool
                .get("org.floggy.synchronization.jme.core.impl.SynchronizableMetadataManager");
        CtMethod[] methods = ctClass.getMethods();

        for (int i = 0; i < methods.length; i++) {
            if (methods[i].getName().equals("init")) {
                ctClass.removeMethod(methods[i]);
            }
        }

        System.out.println(buffer.toString());
        ctClass.addMethod(CtNewMethod.make(buffer.toString(), ctClass));
        embeddedClassesOutputPool.addClass(ctClass);
    }

    /**
    * DOCUMENT ME!
    *
    * @param ctClass DOCUMENT ME!
    *
    * @return DOCUMENT ME!
    *
    * @throws NotFoundException DOCUMENT ME!
    */
    protected List buildClassTree(CtClass ctClass) throws NotFoundException {
        final CtClass synchronizable = classpathPool.get(Weaver.SYNCHRONIZABLE_CLASSNAME);
        List list = new ArrayList();
        CtClass superClass = ctClass;
        String superName = ctClass.getName();

        do {
            list.add(superName);
            superClass = superClass.getSuperclass();
            superName = superClass.getName();
        } while (!"java.lang.Object".equals(superName) && superClass.subtypeOf(synchronizable));

        Collections.reverse(list);

        for (Iterator iterator = list.iterator(); iterator.hasNext();) {
            String className = (String) iterator.next();

            if (!configuration.containsSynchronizable(className)) {
                CtClass clazz = classpathPool.get(className);
                configuration.addSynchronizableMetadata(createSynchronizableMetadata(clazz));
            }
        }

        return list;
    }

    /**
    * DOCUMENT ME!
    *
    * @param fieldType DOCUMENT ME!
    *
    * @return DOCUMENT ME!
    *
    * @throws NotFoundException DOCUMENT ME!
    */
    protected Integer buildFloggyFieldType(CtClass fieldType) throws NotFoundException {
        int floggyFieldType = 0;

        if (fieldType.isArray()) {
            fieldType = fieldType.getComponentType();

            if (fieldType.isPrimitive()) {
                floggyFieldType = SynchronizableMetadata.PRIMITIVE | buildFloggyPrimitiveFieldType(fieldType);
            } else {
                floggyFieldType = buildFloggyObjectFieldType(fieldType);
            }

            floggyFieldType = floggyFieldType | SynchronizableMetadata.ARRAY;
        } else {
            if (fieldType.isPrimitive()) {
                floggyFieldType = SynchronizableMetadata.PRIMITIVE | buildFloggyPrimitiveFieldType(fieldType);
            } else {
                floggyFieldType = buildFloggyObjectFieldType(fieldType);
            }
        }

        if (floggyFieldType == 0) {
            throw new NotFoundException(fieldType.getName());
        }

        return Integer.valueOf(floggyFieldType);
    }

    /**
    * DOCUMENT ME!
    *
    * @param fieldType DOCUMENT ME!
    *
    * @return DOCUMENT ME!
    *
    * @throws NotFoundException DOCUMENT ME!
    */
    protected int buildFloggyObjectFieldType(CtClass fieldType) throws NotFoundException {
        int floggyFieldType = 0;
        String typeName = fieldType.getName();

        if ("java.lang.Boolean".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.BOOLEAN;
        }

        if ("java.lang.Byte".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.BYTE;
        }

        if ("java.lang.Character".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.CHARACTER;
        }

        if ("java.lang.Double".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.DOUBLE;
        }

        if ("java.lang.Float".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.FLOAT;
        }

        if ("java.lang.Integer".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.INT;
        }

        if ("java.lang.Long".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.LONG;
        }

        if ("java.lang.Short".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.SHORT;
        }

        if ("java.lang.String".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.STRING;
        }

        if ("java.util.Calendar".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.CALENDAR;
        }

        if ("java.util.Date".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.DATE;
        }

        if ("java.util.Hashtable".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.HASHTABLE;
        }

        if (fieldType.subtypeOf(fieldType.getClassPool().get(Weaver.SYNCHRONIZABLE_CLASSNAME))) {
            floggyFieldType = SynchronizableMetadata.SYNCHRONIZABLE;
        }

        if ("java.lang.StringBuffer".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.STRINGBUFFER;
        }

        if ("java.util.Stack".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.STACK;
        }

        if ("java.util.TimeZone".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.TIMEZONE;
        }

        if ("java.util.Vector".equals(typeName)) {
            floggyFieldType = SynchronizableMetadata.VECTOR;
        }

        return floggyFieldType;
    }

    /**
    * DOCUMENT ME!
    *
    * @param fieldType DOCUMENT ME!
    *
    * @return DOCUMENT ME!
    */
    protected int buildFloggyPrimitiveFieldType(CtClass fieldType) {
        int floggyFieldType = 0;

        if (CtClass.booleanType.equals(fieldType)) {
            floggyFieldType = SynchronizableMetadata.BOOLEAN;
        }

        if (CtClass.byteType.equals(fieldType)) {
            floggyFieldType = SynchronizableMetadata.BYTE;
        }

        if (CtClass.charType.equals(fieldType)) {
            floggyFieldType = SynchronizableMetadata.CHARACTER;
        }

        if (CtClass.doubleType.equals(fieldType)) {
            floggyFieldType = SynchronizableMetadata.DOUBLE;
        }

        if (CtClass.floatType.equals(fieldType)) {
            floggyFieldType = SynchronizableMetadata.FLOAT;
        }

        if (CtClass.intType.equals(fieldType)) {
            floggyFieldType = SynchronizableMetadata.INT;
        }

        if (CtClass.longType.equals(fieldType)) {
            floggyFieldType = SynchronizableMetadata.LONG;
        }

        if (CtClass.shortType.equals(fieldType)) {
            floggyFieldType = SynchronizableMetadata.SHORT;
        }

        return floggyFieldType;
    }

    /**
    * DOCUMENT ME!
    *
    * @param className DOCUMENT ME!
    * @param fieldName DOCUMENT ME!
    *
    * @return DOCUMENT ME!
    */
    protected boolean containsField(String className, String fieldName) {
        try {
            CtClass ctClass = classpathPool.get(className);
            ctClass.getField(fieldName);

            return true;
        } catch (NotFoundException e) {
            return false;
        }
    }

    /**
    * DOCUMENT ME!
    *
    * @param fileName DOCUMENT ME!
    *
    * @throws IOException DOCUMENT ME!
    */
    protected void embeddedClass(String fileName) throws IOException {
        URL fileURL = getClass().getResource(fileName);

        if (fileURL != null) {
            fileName = fileName.replace('/', File.separatorChar);
            embeddedClassesOutputPool.addFile(fileURL, fileName);
            classpathPool.makeClass(fileURL.openStream());
        }
    }

    /**
    * DOCUMENT ME!
    *
    * @throws IOException DOCUMENT ME!
    */
    protected void embeddedUnderlineCoreClasses() throws IOException {
        embeddedClass("/org/floggy/org/json/me/JSONArray.class");
        embeddedClass("/org/floggy/org/json/me/JSONException.class");
        embeddedClass("/org/floggy/org/json/me/JSONObject.class");
        embeddedClass("/org/floggy/org/json/me/JSONObject$1.class");
        embeddedClass("/org/floggy/org/json/me/JSONObject$Null.class");
        embeddedClass("/org/floggy/org/json/me/JSONString.class");
        embeddedClass("/org/floggy/org/json/me/JSONStringer.class");
        embeddedClass("/org/floggy/org/json/me/JSONTokener.class");
        embeddedClass("/org/floggy/org/json/me/JSONWriter.class");
        embeddedClass("/org/floggy/org/json/me/StringWriter.class");
        embeddedClass("/org/floggy/synchronization/jme/core/impl/JSONSerializationManager.class");
        embeddedClass("/org/floggy/synchronization/jme/core/impl/__Synchronizable.class");
        embeddedClass("/org/floggy/synchronization/jme/core/impl/SynchronizableMetadata.class");
        embeddedClass("/org/floggy/synchronization/jme/core/impl/SynchronizableMetadataManager.class");
        embeddedClass("/org/floggy/synchronization/jme/core/impl/SynchronizationManagerImpl.class");
        embeddedClass("/org/floggy/synchronization/jme/core/impl/Utils.class");
        embeddedClass("/org/json/synchronization/jme/core/impl/Utils.class");
    }

    /**
    * Returns the class name given a file name.
    *
    * @param fileName
    *
    * @return
    */
    protected String getClassName(String fileName) {
        if (fileName.endsWith(".class")) {
            String className = fileName.replace(File.separatorChar, '.');

            return className.substring(0, className.length() - 6);
        }

        return null;
    }

    /**
    * DOCUMENT ME!
    *
    * @return DOCUMENT ME!
    *
    * @throws NotFoundException DOCUMENT ME!
    * @throws IOException DOCUMENT ME!
    */
    protected List getClassThatImplementsSynchronizable() throws NotFoundException, IOException {
        int classCount = this.inputPool.getFileCount();
        LOG.info("Look up for classes that implements Synchronizable!");

        List list = new LinkedList();
        final CtClass synchronizable = classpathPool.get(Weaver.SYNCHRONIZABLE_CLASSNAME);
        final CtClass __synchronizable = classpathPool.get(Weaver.__SYNCHRONIZABLE_CLASSNAME);

        for (int i = 0; i < classCount; i++) {
            String fileName = this.inputPool.getFileName(i);
            String className = getClassName(fileName);

            if (className == null) {
                this.outputPool.addFile(inputPool.getFileURL(i), fileName);

                continue;
            }

            CtClass ctClass = this.classpathPool.get(className);

            if (ctClass.subtypeOf(synchronizable) && !ctClass.subtypeOf(__synchronizable)
                    && !ctClass.isInterface()) {
                List tree = buildClassTree(ctClass);

                for (int j = 0; j < tree.size(); j++) {
                    Object object = tree.get(j);

                    if (!list.contains(object)) {
                        list.add(object);
                    }
                }
            } else {
                LOG.debug("Bytecode NOT modified.");

                if (ctClass.subtypeOf(__synchronizable) && !ctClass.equals(__synchronizable)) {
                    List temp = buildClassTree(ctClass);
                    Iterator iterator = temp.iterator();

                    while (iterator.hasNext()) {
                        className = (String) iterator.next();

                        SynchronizableMetadata metadata = configuration.getSynchronizableMetadata(className);
                        alreadyProcessedMetadatas.add(metadata);
                    }
                }

                this.outputPool.addFile(inputPool.getFileURL(i), fileName);
            }
        }

        return list;
    }

    /**
    * DOCUMENT ME!
    *
    * @param field DOCUMENT ME!
    *
    * @return DOCUMENT ME!
    */
    protected boolean ignoreField(CtField field) {
        int modifier = field.getModifiers();

        return field.getName().equals("__id") || Modifier.isTransient(modifier) || Modifier.isStatic(modifier);
    }

    /**
    * DOCUMENT ME!
    *
    * @return DOCUMENT ME!
    */
    protected boolean isCLDC10() {
        try {
            CtClass ctClass = classpathPool.get("java.io.DataInput");
            ctClass.getMethod("readFloat", "()F");
        } catch (NotFoundException nfex) {
            return true;
        }

        return false;
    }
}