padl.kernel.impl.Constituent.java Source code

Java tutorial

Introduction

Here is the source code for padl.kernel.impl.Constituent.java

Source

/*******************************************************************************
 * Copyright (c) 2001-2014 Yann-Gal Guhneuc and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Yann-Gal Guhneuc and others, see in file; API and its implementation
 ******************************************************************************/
package padl.kernel.impl;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.commons.lang.ArrayUtils;
import padl.kernel.Constants;
import padl.kernel.IConstituent;
import padl.kernel.IConstituentExtension;
import padl.kernel.exception.ModelDeclarationException;
import padl.util.Util;
import padl.visitor.IVisitor;
import util.io.ProxyConsole;
import util.lang.Modifier;
import util.multilingual.MultilingualManager;
import com.ibm.toad.cfparse.utils.Access;

// TODO: Should we provide to people extending PADL some default
// implementation of the different Constituent of the meta-model?
// Maybe class "Constituent" could be public, or classes "Elements"
// and "Entities"?
public abstract class Constituent implements IConstituent {
    private static IVisitor cachedCurrentVisitor;
    private static final Map cachedAcceptClassNames = new HashMap();
    private static final Map cachedCorrespondingInterfaceNames = new HashMap();
    private static final List cachedNotFoundClassNames = new ArrayList();
    private static final long serialVersionUID = -3089376780834846438L;

    private Constituent clone;
    // Yann 2009/04/29: Useless?
    // It seems that the cloning protocol changed
    // so much that these variable are not useful.
    // private List clonedBoundEventList = new ArrayList(0);
    // private List clonedVetoEventList = new ArrayList(0);
    private String[] codeLines;
    private String comment;
    // Yann 2004/04/09: Why "extends AbstractContainer"?
    // From the point of view of the model (defined by the
    // interfaces in the kernel), only certain constituent
    // may hold other constituent (by implementing IContainer).
    // From the point of view of the implementation and to
    // avoid duplication, any constituent may hold other
    // constituent. This is not a problem because a model
    // can only be manipulated through the interfaces.
    private char[] id;
    private char[] name;
    private char[] path;
    private int visibility = Access.ACC_PUBLIC;
    private int weight = Constants.MANDATORY;
    private int numberOfExtensions;
    private IConstituentExtension[] extensions;
    private int indexInPrimeNumbersArray;

    public Constituent(final char[] anID) {
        if (anID == null) {
            throw new ModelDeclarationException(MultilingualManager.getString("ACTOR_ID_NULL", IConstituent.class));
        }
        this.id = anID;
        this.setName(anID);
        this.setPath(anID);
    }

    public void accept(final IVisitor visitor) {
        this.accept(visitor, "visit");
    }

    protected void accept(final IVisitor visitor, final String methodName) {
        // Yann 2015/05/24: Cache reset...
        // If the visitor has changed, then I reset the cache.
        if (cachedCurrentVisitor != visitor) {
            cachedAcceptClassNames.clear();
            cachedCorrespondingInterfaceNames.clear();
            cachedCurrentVisitor = visitor;
            cachedNotFoundClassNames.clear();
        }

        this.accept(this.getClass(), visitor, methodName, true);
    }

    private boolean accept(final java.lang.Class currentReceiver, final IVisitor visitor, final String methodName,
            final boolean shouldRecurse) {

        // Yann 2003/12/05: Interfaces!
        // I must match a class a the kernel to the
        // corresponding interface:
        // padl.kernel.kernel.impl.Association
        // ->
        // padl.kernel.IAssociation
        // Yann 2010/06/21: Performance
        // The CreatorJCT spent a lot of time in this method,
        // in which the call to String.replaceAll() was
        // accounting for most of the time spent. I added a
        // cache to avoid doing the same computations again
        // and over again.
        final String className = currentReceiver.getName();
        if (!Constituent.cachedAcceptClassNames.containsKey(className)) {
            Constituent.cachedAcceptClassNames.put(className, className.replaceAll(".impl.", ".I"));
        }
        String acceptClassName = (String) cachedAcceptClassNames.get(className);

        java.lang.Class argument = null;
        if (!Constituent.cachedNotFoundClassNames.contains(acceptClassName)) {
            if (Constituent.cachedCorrespondingInterfaceNames.containsKey(acceptClassName)) {

                acceptClassName = (String) Constituent.cachedCorrespondingInterfaceNames.get(acceptClassName);
            }

            try {
                // Old : doesn't work with bundle
                // final java.lang.Class[] argument =
                // new java.lang.Class[] { java.lang.Class
                // .forName(acceptClassName) };
                // TODO understand the difference between forName and loadClass...
                argument = visitor.getClass().getClassLoader().loadClass(acceptClassName);
            } catch (final ClassNotFoundException e) {
                Constituent.cachedNotFoundClassNames.add(acceptClassName);
                visitor.unknownConstituentHandler(methodName, this);
                return false;
            }
        } else {
            return false;
        }

        try {
            final Method method = visitor.getClass().getMethod(methodName, new java.lang.Class[] { argument });
            method.invoke(visitor, new Object[] { this });
            return true;
        } catch (final Exception e) {
            // Yann 2004/04/10: New constituents!
            // In case I add new constituent and forget to update
            // the method in the IVisitor interface, I forward such
            // exceptions (to fail the appropriate tests).
            // System.err.println(MultilingualManager.getString(
            // "Exception_ACCEPT_METHOD",
            // Constituent.class,
            // new Object[] { methodName, acceptMethodName }));
            // e.printStackTrace();
            // Yann 2008/10/13: Fall back!
            // Following Mathieu's suggesting, the visiting of
            // an unknown constituent (by the visitor) won't
            // fail anymore but "gracefully" call an handler
            // method that should deal with it!
            //
            // Yann 2013/06/17: Interesting!
            // Here is an interesting mix between bypassing the 
            // look-up mechanisms of the compiler and of the
            // virtual machine: ICPPClass is a sub-interface of
            // IClass, so if a IVisitor does not know what to do
            // with ICPPClass, it may know with IClass...
            // WARNING! I only consider the immediate super-
            // interfaces even though the code is recursive...
            boolean foundACandidateReceiver = false;
            if (shouldRecurse) {
                final java.lang.Class[] interfaces = argument.getInterfaces();
                int i = 0;
                while (i < interfaces.length && !foundACandidateReceiver) {
                    final java.lang.Class interfase = interfaces[i];
                    foundACandidateReceiver = this.accept(interfase, visitor, methodName, false);
                    i++;
                }
                if (foundACandidateReceiver) {
                    i = i - 1;
                    Constituent.cachedCorrespondingInterfaceNames.put(acceptClassName, interfaces[i].getName());
                    return true;
                } else {
                    Constituent.cachedNotFoundClassNames.add(acceptClassName);
                    visitor.unknownConstituentHandler(methodName + '(' + argument.getName() + ')', this);
                }
            }
        }

        return false;
    }

    public final void addExtension(final IConstituentExtension anExtension) {
        if (anExtension instanceof IConstituentExtension) {
            final int minCapacity = this.numberOfExtensions + 1;
            final int oldCapacity = this.extensions.length;
            if (minCapacity > oldCapacity) {
                final IConstituentExtension[] oldData = this.extensions;
                this.indexInPrimeNumbersArray++;
                int newCapacity = GenericContainerConstants.PRIME_NUMBERS[this.indexInPrimeNumbersArray];
                if (newCapacity < minCapacity)
                    newCapacity = minCapacity;
                this.extensions = new IConstituentExtension[newCapacity];
                System.arraycopy(oldData, 0, this.extensions, 0, this.numberOfExtensions);
            }
            this.extensions[this.numberOfExtensions] = anExtension;
            this.numberOfExtensions++;

            anExtension.setExtendedConstituent(this);
        } else {
            throw new ModelDeclarationException(
                    anExtension.getClass().getName() + " must be a subtype of IConstituentExtension");
        }
    }

    public void endCloneSession() {
        this.clone = null;
        // this.clonedBoundEventList.clear();
        // this.clonedVetoEventList.clear();
    }

    public boolean equals(final Object obj) {
        if (!(obj instanceof IConstituent)) {
            return false;
        }
        return Arrays.equals(this.getID(), ((IConstituent) obj).getID());
    }

    public IConstituent getClone() {
        return this.clone;
    }

    public String[] getCodeLines() {
        return this.codeLines;
    }

    public String getComment() {
        return this.comment;
    }

    public String getDisplayID() {
        return String.valueOf(this.getID());
    }

    public String getDisplayName() {
        return String.valueOf(this.getName());
    }

    public String getDisplayPath() {
        return String.valueOf(this.getPath());
    }

    public final IConstituentExtension getExtension(final char[] anExtensionName) {
        for (int i = 0; i < this.extensions.length; i++) {
            final IConstituentExtension extension = this.extensions[i];
            if (Arrays.equals(extension.getName(), anExtensionName)) {
                return extension;
            }
        }
        return null;
    }

    public char[] getID() {
        return this.id;
    }

    public char[] getName() {
        return this.name;
    }

    public char[] getPath() {
        return this.path;
    }

    protected abstract char getPathSymbol();

    public int getVisibility() {
        return this.visibility;
    }

    public int getWeight() {
        return this.weight;
    }

    public int hashCode() {
        // Yann 2013/09/11: I got caught!
        // I naively thought that the following call
        //   return this.getID().hashCode();
        // was correct to implement the hashCode but
        // it is not so! See
        //   http://stackoverflow.com/questions/744735/java-array-hashcode-implementation
        return ArrayUtils.hashCode(this.getID());
    }

    public boolean isAbstract() {
        return Access.isAbstract(this.visibility);
    }

    public boolean isFinal() {
        return Access.isFinal(this.visibility);
    }

    public boolean isPrivate() {
        return Access.isPrivate(this.visibility);
    }

    public boolean isProtected() {
        return Access.isProtected(this.visibility);
    }

    public boolean isPublic() {
        return Access.isPublic(this.visibility);
    }

    public boolean isStatic() {
        return Access.isStatic(this.visibility);
    }

    /**
     * This methods is used by the clone protocol.
     */
    public void performCloneSession() {
    }

    public void resetCodeLines() {
        this.setCodeLines("");
    }

    public void setAbstract(final boolean aBoolean) {
        this.setVisibility(aBoolean ? (this.getVisibility() | Access.ACC_ABSTRACT)
                : (this.getVisibility() & ~Access.ACC_ABSTRACT));
    }

    public void setCodeLines(final String someCode) {
        final List listOfLines = new ArrayList();
        final StringTokenizer tokeniser = new StringTokenizer(someCode, "\n\r");
        while (tokeniser.hasMoreTokens()) {
            final String line = (String) tokeniser.nextElement();
            listOfLines.add(line);
        }
        final String[] arrayOfLines = new String[listOfLines.size()];
        listOfLines.toArray(arrayOfLines);
        this.setCodeLines(arrayOfLines);
    }

    public void setCodeLines(String[] code) {
        if (this.isAbstract()) {
            throw new ModelDeclarationException(MultilingualManager.getString("ELEM_CODE_DEF", IConstituent.class));
        }
        this.codeLines = code;
    }

    public void setComment(final String aComment) {
        this.comment = aComment;
    }

    public void setDisplayName(final String aName) {
        this.name = aName.toCharArray();
    }

    public void setFinal(final boolean aBoolean) {
        this.setVisibility(
                aBoolean ? (this.getVisibility() | Access.ACC_FINAL) : (this.getVisibility() & ~Access.ACC_FINAL));
    }

    protected void setID(final char[] anID) {
        this.id = anID;
    }

    public void setName(final char[] aName) {
        this.name = aName;
    }

    void setPath(final char[] aPath) {
        this.path = aPath;
    }

    public void setPrivate(final boolean aBoolean) {
        this.setVisibility(aBoolean
                ? ((this.getVisibility() | Access.ACC_PRIVATE) & ~(Access.ACC_PUBLIC | Access.ACC_PROTECTED))
                : (this.getVisibility() & ~Access.ACC_PRIVATE));
    }

    public void setProtected(final boolean aBoolean) {
        this.setVisibility(aBoolean
                ? ((this.getVisibility() | Access.ACC_PROTECTED) & ~(Access.ACC_PUBLIC | Access.ACC_PRIVATE))
                : (this.getVisibility() & ~Access.ACC_PROTECTED));
    }

    public void setPublic(final boolean aBoolean) {
        this.setVisibility(aBoolean
                ? ((this.getVisibility() | Access.ACC_PUBLIC) & ~(Access.ACC_PRIVATE | Access.ACC_PROTECTED))
                : (this.getVisibility() & ~Access.ACC_PUBLIC));
    }

    public void setStatic(final boolean aBoolean) {
        this.setVisibility(aBoolean ? (this.getVisibility() | Access.ACC_STATIC)
                : (this.getVisibility() & ~Access.ACC_STATIC));
    }

    public void setVisibility(final int visibility) {
        if (this.getCodeLines() != null && Access.isAbstract(visibility)) {
            // Why test getCodeLines() != null here ?
            throw new ModelDeclarationException(MultilingualManager.getString("ELEM_ABSTRACT", IConstituent.class));
        }
        this.visibility = visibility;
    }

    public void setWeight(final int weight) {
        this.weight = weight;
    }

    /**
     * This method performs a shallow copy.
     */
    public void startCloneSession() {
        try {
            final Constituent tmpObject = (Constituent) super.clone();
            this.clone = tmpObject;
            tmpObject.clone = null;

            // Yann 2010/10/03: Paths!
            // The "path" of each cloned constituent
            // must be reset of course because the
            // clone does not yet belong to any model.
            // Yann 2013/10/24: Miracle?!
            // It is a miracle that cloning did work sometimes...
            // Of course the clone should start with it ID as
            // minimum path to make sure that it has a path!
            //   this.clone.setPath(ArrayUtils.EMPTY_CHAR_ARRAY);
            this.clone.setPath(this.id);
        } catch (final CloneNotSupportedException cnse) {
            cnse.printStackTrace(ProxyConsole.getInstance().errorOutput());
        }
    }

    public String toString() {
        return this.toString(0);
    }

    public String toString(final int tab) {
        final StringBuffer codeEq = new StringBuffer();
        Util.addTabs(tab, codeEq);
        if (this.getComment() != null) {
            codeEq.append("/* ");
            codeEq.append(this.getComment());
            codeEq.append(" */\n");
            Util.addTabs(tab, codeEq);
        }
        codeEq.append(Modifier.toString(this.getVisibility()));
        return codeEq.toString();
    }
}