com.dragome.compiler.units.ClassUnit.java Source code

Java tutorial

Introduction

Here is the source code for com.dragome.compiler.units.ClassUnit.java

Source

/*******************************************************************************
 * Copyright (c) 2011-2014 Fernando Petrola
 * 
 * This file is part of Dragome SDK.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 ******************************************************************************/

// Copyright 2011 The j2js Authors. All Rights Reserved.
//
// This file is part of j2js.
//
// j2js is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// j2js 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 Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with j2js. If not, see <http://www.gnu.org/licenses/>.

package com.dragome.compiler.units;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.InvocationHandler;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.io.output.StringBuilderWriter;

import com.dragome.compiler.DragomeJsCompiler;
import com.dragome.compiler.Project;
import com.dragome.compiler.generators.DragomeJavaScriptGenerator;
import com.dragome.compiler.type.Signature;
import com.dragome.compiler.type.TypeCollector;
import com.dragome.compiler.utils.FileObject;
import com.dragome.compiler.utils.Log;

public class ClassUnit extends Unit {
    public static final String STATIC_MEMBER = "#static-member#";

    static final long serialVersionUID = 1;

    private long lastCompiled;

    public long getLastModified() {
        return getClassFile().getLastModified();
    }

    private Map<String, MemberUnit> declaredMembers;

    private ClassUnit superUnit;

    private Collection<ClassUnit> interfaces;

    private Collection<ClassUnit> subUnits;

    public boolean isInterface = false;

    public boolean isConstructorTainted = false;

    public Map<String, String>[] annotations;

    private Project project;

    private transient boolean isResolved = false;

    private transient FileObject classFile;

    private List<ClassUnit> implementors = new ArrayList<ClassUnit>();

    private transient boolean written = false;

    private String generatedJs;

    public static boolean oneWritten = false;

    public List<ClassUnit> getImplementors() {
        return implementors;
    }

    public void setImplementors(List<ClassUnit> implementors) {
        this.implementors = implementors;
    }

    public static List<MemberUnit> stringInits = new ArrayList<MemberUnit>();

    public ClassUnit() {
    }

    public ClassUnit(Project theProject, Signature theSignature) {
        project = theProject;

        interfaces = new LinkedHashSet<ClassUnit>();
        declaredMembers = new LinkedHashMap<String, MemberUnit>();
        subUnits = new LinkedHashSet<ClassUnit>();
        lastCompiled = -1;
        setSignature(theSignature);
    }

    public void clear() {
        lastCompiled = -1;
        removeInterfaces();
        setSuperUnit(null);
        declaredMembers.clear();
        generatedJs = null;
    }

    public boolean isUpToDate() {
        return lastCompiled >= getLastModified();
    }

    public Collection<ClassUnit> getInterfaces() {
        return interfaces;
    }

    public void addInterface(ClassUnit interfaceUnit) {
        interfaces.add(interfaceUnit);
        interfaceUnit.addImplementor(this);
    }

    private void addImplementor(ClassUnit classUnit) {
        implementors.add(classUnit);
    }

    private void removeInterfaces() {
        Iterator iter = interfaces.iterator();
        while (iter.hasNext()) {
            ClassUnit interfaceUnit = (ClassUnit) iter.next();
            interfaceUnit.removeSubUnit(this);
            iter.remove();
        }
    }

    public void addSubUnit(ClassUnit subUnit) {
        if (subUnit == null)
            throw new NullPointerException();
        subUnits.add(subUnit);
    }

    public void removeSubUnit(ClassUnit subUnit) {
        if (subUnit == null)
            throw new NullPointerException();
        subUnits.remove(subUnit);
    }

    public Collection<ClassUnit> getSubUnits() {
        return subUnits;
    }

    public MemberUnit getDeclaredMember(String signature) {
        if (signature == null)
            throw new NullPointerException();
        return declaredMembers.get(signature);
    }

    public Collection<ClassUnit> getSupertypes() {
        TypeCollector collector = new TypeCollector();
        project.visitSuperTypes(this, collector);
        return collector.collectedTypes;
    }

    public Collection<MemberUnit> getMembers(String signature) {
        if (signature == null)
            throw new NullPointerException();
        ArrayList<MemberUnit> list = new ArrayList<MemberUnit>();

        for (ClassUnit clazz : getSupertypes()) {
            MemberUnit member = clazz.getDeclaredMember(signature);
            if (member != null)
                list.add(member);
        }

        return list;
    }

    public Collection<MemberUnit> getDeclaredMembers() {
        if (!isResolved)
            throw new RuntimeException("Class is not yet resolved: " + getName());
        return declaredMembers.values();
    }

    public void addMemberUnit(MemberUnit unit) {
        declaredMembers.put(unit.getSignature().toString(), unit);
    }

    public ClassUnit getSuperUnit() {
        return superUnit;
    }

    public void setSuperUnit(ClassUnit theSuperUnit) {
        if (superUnit != null) {
            superUnit.removeSubUnit(this);
        }
        superUnit = theSuperUnit;
        if (superUnit != null) {
            superUnit.addSubUnit(this);
        }
    }

    public void write(int depth, Writer writer2) throws IOException {
        if (!isTainted() || !isResolved() || isWritten())
            return;

        Writer writer = new StringBuilderWriter();

        oneWritten = true;
        written = true;

        for (ClassUnit child : getSupertypes()) {
            child.setTainted();
            child.write(depth, writer2);
        }

        if (getSuperUnit() != null) {
            getSuperUnit().setTainted();
            getSuperUnit().write(depth, writer2);
        }

        for (ClassUnit child : getInterfaces()) {
            child.setTainted();
            child.write(depth, writer2);
        }

        if (generatedJs == null)
            generatedJs = generateJsCode(depth, writer);

        writer2.write(generatedJs);

        taintRelated(depth, writer2);
    }

    private String generateJsCode(int depth, Writer writer) throws IOException {
        Log.getLogger().debug(getIndent(depth) + this);

        if (getData() != null) {
            writer.write(getData());
        } else {
            throw new RuntimeException("Cannot compile the class: " + getName());
        }

        Set<MemberUnit> notImplementedMethods = getNotImplementedMethods();

        if (interfaces.size() > 0) {
            String extendOperator = "implement";
            if (isInterface)
                extendOperator = "extend";
            //       writer.write("_T.interfaces = [");
            writer.write(extendOperator + ": [");
            int i = 0;
            for (ClassUnit interFace : interfaces) {
                if (i++ > 0)
                    writer.write(", ");
                writer.write(DragomeJavaScriptGenerator.normalizeExpression(interFace.getSignature().toString()));
            }
            writer.write("],\n");
        }

        writer.write("members:\n");
        writer.write("{\n");

        MemberUnit clinitMethod = null;
        List<MemberUnit> staticMethods = new ArrayList<MemberUnit>();

        boolean first = true;

        for (MemberUnit member : getDeclaredMembers()) {
            if (member.getData() != null && member.getData().startsWith(STATIC_MEMBER)) {
                staticMethods.add(member);
            } else if (member.getData() != null && member.getData().contains("_dragomeJs.StringInit")) {
                stringInits.add(member);
            } else {
                if (member.getData() != null && member.getData().trim().length() > 0) {
                    if (!first)
                        writer.write(",\n");
                    first = false;

                    writeMethodAlternative(depth, writer, member);

                    if (member instanceof ProcedureUnit) {
                        project.currentGeneratedMethods++;
                        writer.flush();
                    }
                }
            }
            if (isClinit(member))
                clinitMethod = member;
        }

        for (MemberUnit member : notImplementedMethods) {
            if (!first)
                writer.write(",\n");

            first = false;

            String methodData = member.getData().replace(STATIC_MEMBER, "");
            methodData = methodData.substring(0, methodData.indexOf("{"));
            writer.write(methodData);
            writer.write("{\n return ");
            writer.write(member.getDeclaringClass().toString().replace(".", "_"));
            writer.write(".$$members.");
            writer.write(
                    methodData.replace(": function ()", ".call (this)").replace(": function (", ".call (this, "));
            writer.write("}\n");

            if (member instanceof ProcedureUnit) {
                project.currentGeneratedMethods++;
                writer.flush();
            }
        }

        writer.write("\n},\n");
        writer.write("statics:\n");
        writer.write("{\n");

        if (clinitMethod != null) {
            String name = DragomeJavaScriptGenerator
                    .normalizeExpression(clinitMethod.getDeclaringClass().getName());
            String replace = clinitMethod.getData().replace("this.", name + ".");
            replace = replace.replace("{", "{ this.$$clinit_=function(){return this};");
            replace = replace.substring(0, replace.length() - 2) + "\n return this;\n}";

            String memberData = clinitMethod.getData();
            clinitMethod.setData(replace.replace(STATIC_MEMBER, ""));
            clinitMethod.write(depth, writer);
            clinitMethod.setData(memberData);
            String modifyMethodName = DragomeJavaScriptGenerator.normalizeExpression(clinitMethod.getSignature());
            project.getClinits().add("initClass(" + name + ");");
        } else {
            String replace = "$$clinit_: function(){return this}";
            writer.write(replace);
        }

        boolean hasStaticMembers = staticMethods.size() > 0;
        if (hasStaticMembers)
            writer.write(",");

        writer.write("\n");

        first = true;

        for (MemberUnit member : staticMethods) {
            if (member != clinitMethod) {
                if (!first)
                    writer.write(",\n");

                first = false;

                String memberData = member.getData();
                member.setData(member.getData().replace(STATIC_MEMBER, ""));

                writeMethodAlternative(depth, writer, member);

                member.setData(memberData);
                if (member instanceof ProcedureUnit || member instanceof FieldUnit) {
                    if (member instanceof ProcedureUnit)
                        project.currentGeneratedMethods++;

                    writer.flush();
                }
            }
        }

        first = addSuperStaticMethods(writer, !hasStaticMembers ^ first);

        //      addAnnotationsAsStaticMember(writer, first);

        writer.write("\n}\n");

        writer.write("\n}\n\n");
        writer.write(");\n");

        return writer.toString();
    }

    private void addAnnotationsAsStaticMember(Writer writer, boolean first) throws IOException {
        if (!first)
            writer.write(",\n");

        writer.write("{\n");

        for (Entry<String, String> entry : annotationsValues.entrySet()) {
            writer.write(entry.getKey() + "\n");
            writer.write(entry.getValue() + "\n");
        }

        writer.write("}\n");
    }

    private Set<MemberUnit> getNotImplementedMethods() {
        Set<MemberUnit> interfacesMembers = new HashSet<MemberUnit>();
        getDeclaredMembersInInterfaces(this, interfacesMembers);

        Set<MemberUnit> implementedMembers = new HashSet<MemberUnit>();
        getImplementedMembersInHierarchy(this, implementedMembers);

        interfacesMembers.removeAll(implementedMembers);
        if (isImplementing(InvocationHandler.class) || isAbstract || isInterface || interfacesMembers.size() <= 0
                || interfacesMembers.isEmpty())
            interfacesMembers.clear();

        return interfacesMembers;
    }

    private boolean isImplementing(Class<InvocationHandler> class1) {
        if (getSuperUnit() != null && getSuperUnit().isImplementing(class1))
            return true;

        for (ClassUnit interfaz : getInterfaces()) {
            if (interfaz.toString().equals(class1.getName()) || interfaz.isImplementing(class1))
                return true;
        }
        return false;
    }

    private void getImplementedMembersInHierarchy(ClassUnit classUnit, Collection<MemberUnit> implementedMembers) {
        if (classUnit != null) {
            if (isInterface)
                return;

            implementedMembers.addAll(filterMethods(classUnit.getDeclaredMembers()));
            getImplementedMembersInHierarchy(classUnit.getSuperUnit(), implementedMembers);
        }
    }

    private Collection<MethodUnit> filterMethods(Collection<MemberUnit> declaredMembers2) {
        Collection<MethodUnit> result = new ArrayList<MethodUnit>();
        for (MemberUnit memberUnit : declaredMembers2) {
            if (memberUnit instanceof MethodUnit)
                result.add((MethodUnit) memberUnit);
        }
        return result;
    }

    private void getDeclaredMembersInInterfaces(ClassUnit classUnit, Collection<MemberUnit> interfacesMembers) {
        if (classUnit != null) {
            if (isInterface)
                interfacesMembers.addAll(filterMethods(classUnit.getDeclaredMembers()));

            for (ClassUnit interfaceUnit : classUnit.getInterfaces()) {
                interfacesMembers.addAll(filterMethods(interfaceUnit.getDeclaredMembers()));
                interfaceUnit.getDeclaredMembersInInterfaces(interfaceUnit, interfacesMembers);
            }

            getDeclaredMembersInInterfaces(classUnit.getSuperUnit(), interfacesMembers);
        }
    }

    private boolean isClinit(MemberUnit member) {
        return member.getSignature().toString().equals("<clinit>()void");
    }

    private boolean addSuperStaticMethods(Writer writer, boolean first) throws IOException {
        if (superUnit != null)
            for (MemberUnit member : superUnit.getDeclaredMembers()) {
                if (member.getData() != null && member.getData().startsWith(STATIC_MEMBER)
                        && member instanceof MethodUnit && !isClinit(member)) {
                    String methodData = member.getData().replace(STATIC_MEMBER, "");
                    String substring = methodData.substring(0, methodData.indexOf("{"));
                    String methodName = substring.replace(": function", "").replace("\n", "");
                    substring += "{ \n\t return this.superclass." + methodName + ";\n}";

                    if (!first)
                        writer.write(",\n");

                    writer.write(substring);
                    first = false;
                }
            }

        return first;
    }

    private void writeMethodAlternative(int depth, Writer writer, MemberUnit member) throws IOException {
        if (member instanceof MethodUnit) {
            MethodUnit methodUnit = (MethodUnit) member;
            String normalizedSignature = DragomeJavaScriptGenerator.normalizeExpression(methodUnit.getSignature());
            String normalizedClassname = DragomeJavaScriptGenerator
                    .normalizeExpression(methodUnit.getDeclaringClass().getName());
            Project.getSingleton().getWrittenSignatures().add(normalizedClassname + "|" + normalizedSignature);
        }

        if (member instanceof MethodUnit
                && notReversibleMethods.contains(((MethodUnit) member).getNameAndSignature())) {
            MethodUnit methodUnit = (MethodUnit) member;
            writer.write(extractMethodDefinition(alternativeCompilation, methodUnit.getNameAndSignature()));
        } else
            member.write(depth + 1, writer);
    }

    private String extractMethodDefinition(String compiledCode, String nameAndSignature) {
        int startIndex = compiledCode.indexOf("start of " + nameAndSignature);
        int endIndex = compiledCode.indexOf("end of " + nameAndSignature);

        String part = compiledCode.substring(startIndex, endIndex);
        String result = part.substring(part.indexOf("*/"), part.lastIndexOf("}") + 1);
        result = result.substring(result.indexOf("$"));
        return result;
    }

    private void taintRelated(int depth, Writer writer) throws IOException {
        if (!"java.lang.Object".equals(toString()))
            for (String dependency : dependencies) {
                ClassUnit dependencyClassUnit = project.getOrCreateClassUnit(dependency);
                dependencyClassUnit.setTainted();
                //      dependencyClassUnit.write(depth, writer);
            }

        for (ClassUnit child : getSubUnits()) {
            child.setTainted();
            child.write(depth, writer);
        }

        for (ClassUnit child : getImplementors()) {
            child.setTainted();
            //       child.write(depth, writer);
        }
    }

    private boolean isWritten() {
        return written;
    }

    void setSignature(Signature theSignature) {
        super.setSignature(theSignature);
    }

    public FileObject getClassFile() {
        if (classFile == null) {
            classFile = DragomeJsCompiler.compiler.fileManager
                    .getFileForInput(getSignature().toString().replaceAll("\\.", "/") + ".class");
        }
        return classFile;
    }

    public void setClassFile(FileObject classFile) {
        this.classFile = classFile;
    }

    public void setLastCompiled(long theLastCompiled) {
        lastCompiled = theLastCompiled;
    }

    public boolean isResolved() {
        return isResolved;
    }

    public void setSuperTainted() {
        ClassUnit clazz = this;
        do {
            clazz.setTainted();
            clazz = clazz.getSuperUnit();
        } while (clazz != null);

        for (ClassUnit i : interfaces) {
            i.setSuperTainted();
        }
    }

    public void setResolved(boolean theIsResolved) {
        isResolved = theIsResolved;
    }

    public String getName() {
        return getSignature().className();
    }

    public Project getProject() {
        return project;
    }

    private List<String> dependencies = new ArrayList<String>();

    private byte[] bytecode;

    public byte[] getBytecode() {
        return bytecode;
    }

    private List<String> notReversibleMethods = new ArrayList<String>();

    private String alternativeCompilation;

    public boolean isAbstract = false;

    private Map<String, String> annotationsValues;

    public List<String> getNotReversibleMethods() {
        return notReversibleMethods;
    }

    public void addDependency(String dependency) {
        dependencies.add(dependency);
    }

    public void setBytecodeArrayI(byte[] bytecode) {
        this.bytecode = bytecode;
    }

    public void addNotReversibleMethod(String methodNameSignature) {
        notReversibleMethods.add(methodNameSignature);
    }

    public void setAlternativeCompilation(String alternativeCompilation) {
        this.alternativeCompilation = alternativeCompilation;
    }

    public void setAnnotations(Map<String, String> annotationsValues) {
        this.annotationsValues = annotationsValues;
    }

}