org.nuxeo.ide.connect.OperationScanner.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.ide.connect.OperationScanner.java

Source

/*
 * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     bstefanescu
 */
package org.nuxeo.ide.connect;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IExtendedModifier;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.nuxeo.ide.common.UI;

/**
 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
 *
 */
public class OperationScanner {

    public static final String T_STRING = "string";

    public static final String T_BOOLEAN = "boolean";

    public static final String T_DATE = "date";

    public static final String T_INTEGER = "integer";

    public static final String T_FLOAT = "float";

    public static final String T_RESOURCE = "resource";

    public static final String T_DOCUMENT = "document";

    public static final String T_DOCUMENTS = "documents";

    public static final String T_BLOB = "blob";

    public static final String T_BLOBS = "bloblist";

    public static final String T_SCRIPT = "script";

    public static final String T_PROPERTIES = "properties";

    public static List<OperationModel> getOperations(IJavaProject[] projects) throws Exception {
        List<OperationModel> result = new ArrayList<OperationModel>();
        for (IJavaProject project : projects) {
            for (IPackageFragmentRoot root : project.getPackageFragmentRoots()) {
                if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
                    scanPackageRoot(root, result);
                }
            }
        }
        return result;
    }

    public static List<OperationModel> getOperations(IJavaProject project) throws Exception {
        List<OperationModel> result = new ArrayList<OperationModel>();
        for (IPackageFragmentRoot root : project.getPackageFragmentRoots()) {
            if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
                scanPackageRoot(root, result);
            }
        }
        return result;
    }

    protected static void scanPackageRoot(IPackageFragmentRoot root, Collection<OperationModel> result)
            throws Exception {
        for (Object pkg : root.getChildren()) {
            scanPackage((IPackageFragment) pkg, result);
        }
    }

    protected static void scanPackage(IPackageFragment pkg, Collection<OperationModel> result) throws Exception {
        for (ICompilationUnit unit : pkg.getCompilationUnits()) {
            OperationModel op = tryCompile(unit, null);
            if (op != null) {
                result.add(op);
            }
        }
    }

    /**
     * Get the operation model corresponding to the given compilation unit - if
     * any is found.
     *
     * Try to compile the given compilation unit and return the corresponding
     * operation model if one is found. If the compilation unit doesn't contains
     * any operation - null is returned.
     * <p>
     * Note that the effective compilation is done only if the compilation unit
     * represents an operation.
     *
     * @param unit
     * @return the operation model or null
     */
    public static OperationModel tryCompile(ICompilationUnit unit, IProgressMonitor monitor)
            throws JavaModelException {
        IType[] types = unit.getTypes();
        if (types.length == 0) {
            return null;
        }
        IType type = types[0];
        if (type.getAnnotation("Operation").exists()) {
            ASTNode node = compile(unit, monitor);
            OperationBuilder builder = new OperationBuilder(type);
            try {
                node.accept(builder);
            } catch (Throwable e) {
                UI.showError("Cannot introspect operation " + unit.getElementName(), e);
            }
            return builder.op;
        }
        return null;
    }

    public static ASTNode compile(ICompilationUnit unit, IProgressMonitor monitor) {
        ASTParser parser = ASTParser.newParser(AST.JLS3);
        parser.setResolveBindings(true);
        parser.setSource(unit);
        parser.setStatementsRecovery(true);
        parser.setBindingsRecovery(true);
        return parser.createAST(monitor);
    }

    static class UnknownExpressionTypeException extends RuntimeException {

        private static final long serialVersionUID = 1L;

        public UnknownExpressionTypeException(String message, Throwable cause) {
            super(message, cause);
        }

        public UnknownExpressionTypeException(String message) {
            super(message);
        }

    }

    static class OperationBuilder extends ASTVisitor {

        protected OperationModel op;

        public OperationBuilder(IType type) {
            op = new OperationModel(type);
        }

        @Override
        public boolean visit(TypeDeclaration node) {
            try {
                return doVisit(node);
            } catch (Throwable e) {
                throw new UnknownExpressionTypeException("Cannot extract annotations values in " + node.toString(),
                        e);
            }
        }

        @SuppressWarnings({ "unchecked" })
        protected boolean doVisit(TypeDeclaration node) {
            List<IExtendedModifier> mods = node.modifiers();
            for (IExtendedModifier m : mods) {
                if (m instanceof NormalAnnotation) {
                    NormalAnnotation anno = (NormalAnnotation) m;
                    if (!"Operation".equals(anno.getTypeName().getFullyQualifiedName())) {
                        continue;
                    }
                    List<MemberValuePair> attrs = (List<MemberValuePair>) anno.values();
                    for (MemberValuePair attr : attrs) {
                        String key = attr.getName().getFullyQualifiedName();
                        String value = null;
                        Expression expr = attr.getValue();
                        if (expr instanceof StringLiteral) {
                            value = ((StringLiteral) expr).getLiteralValue();
                        } else if (expr instanceof QualifiedName) {
                            value = (String) ((QualifiedName) expr).resolveConstantExpressionValue();
                        }
                        op.properties.put(key, value);
                    }
                    break;
                }
            }

            // build signature
            MethodDeclaration[] methods = node.getMethods();
            ArrayList<String> sig = new ArrayList<String>();
            for (MethodDeclaration method : methods) {
                mods = method.modifiers();
                for (IExtendedModifier m : mods) {
                    if (m instanceof Annotation) {
                        Annotation anno = (Annotation) m;
                        if (!"OperationMethod".equals(anno.getTypeName().getFullyQualifiedName())) {
                            continue;
                        }

                        List<SingleVariableDeclaration> params = method.parameters();
                        if (params.size() > 1) {
                            // invalid operation - ignore
                            continue;
                        }
                        boolean isIterable = false;
                        if (anno instanceof NormalAnnotation) {
                            for (MemberValuePair attr : (List<MemberValuePair>) ((NormalAnnotation) anno)
                                    .values()) {
                                String key = attr.getName().getFullyQualifiedName();
                                if ("collector".equals(key)) {
                                    isIterable = true;
                                    break;
                                }
                            }
                        }
                        String out = getTypeSignature(method.getReturnType2());
                        String in = params.isEmpty() ? "void" : getTypeSignature(params.get(0).getType());
                        sig.add(in);
                        sig.add(out);
                        if (isIterable) {
                            sig.add(getListType(in));
                            sig.add(getListType(out));
                        }
                    }
                }
            }
            op.signature = sig.toArray(new String[sig.size()]);

            // build params
            FieldDeclaration[] fields = node.getFields();

            for (FieldDeclaration field : fields) {
                mods = field.modifiers();
                for (IExtendedModifier m : mods) {
                    if (m instanceof NormalAnnotation) {
                        NormalAnnotation anno = (NormalAnnotation) m;
                        if ("Param".equals(anno.getTypeName().getFullyQualifiedName())) {
                            OperationModel.Param param = new OperationModel.Param();
                            param.setType(getTypeSignature(field.getType()));
                            List<MemberValuePair> attrs = (List<MemberValuePair>) anno.values();
                            for (MemberValuePair attr : attrs) {
                                String key = attr.getName().getFullyQualifiedName();
                                Expression expr = attr.getValue();
                                if ("name".equals(key)) {
                                    param.setName(extractValue(expr, String.class));
                                } else if ("required".equals(key)) {
                                    param.setRequired(extractValue(expr, Boolean.class));
                                } else if ("widget".equals(key)) {
                                    param.setWidget(extractValue(expr, String.class));
                                } else if ("values".equals(key)) {
                                    List<Expression> values = ((ArrayInitializer) expr).expressions();
                                    ArrayList<String> vlist = new ArrayList<String>();
                                    for (Expression v : values) {
                                        vlist.add(extractValue(v, String.class));
                                    }
                                    param.setValues(vlist.toArray(new String[vlist.size()]));
                                } else if ("order".equals(key)) {
                                    param.setOrder(extractValue(expr, Integer.class));
                                }
                            }
                            op.addParam(param);
                        }
                    }
                }
            }

            return false;
        }

        protected <T> T extractValue(Expression expr, Class<T> clazz) {
            if (expr instanceof QualifiedName) {
                return clazz.cast(((QualifiedName) expr).getQualifier().resolveConstantExpressionValue());
            }
            if (expr instanceof StringLiteral) {
                return clazz.cast(((StringLiteral) expr).getLiteralValue());
            }
            if (expr instanceof NumberLiteral) {
                return clazz.cast(((NumberLiteral) expr).getToken());
            }
            if (expr instanceof BooleanLiteral) {
                return clazz.cast(((BooleanLiteral) expr).booleanValue());
            }
            throw new IllegalArgumentException(
                    "Cannot handle widget value of type " + expr.getClass().getSimpleName());
        }

        public String getTypeSignature(Type type) {
            if (type.isSimpleType()) {
                SimpleType stype = (SimpleType) type;
                String name = stype.getName().getFullyQualifiedName();
                if ("DocumentModel".equals(name) || "DocumentRef".equals(name)) {
                    return T_DOCUMENT;
                } else if ("Blob".equals(name)) {
                    return T_BLOB;
                } else if ("DocumentModelList".equals(name) || "DocumentRefList".equals(name)) {
                    return T_DOCUMENTS;
                } else if ("BlobList".equals(name)) {
                    return T_BLOBS;
                } else if ("URL".equals(name)) {
                    return T_RESOURCE;
                } else if ("Calendar".equals(name)) {
                    return T_DATE;
                } else {
                    return name.toLowerCase();
                }
            } else if (type.isPrimitiveType()) {
                PrimitiveType.Code code = ((PrimitiveType) type).getPrimitiveTypeCode();
                if (code == PrimitiveType.INT) {
                    return "integer";
                } else {
                    return code.toString().toLowerCase();
                }
            }
            return "object";
        }

    }

    public static String getListType(String type) {
        if (T_DOCUMENT.equals(type)) {
            return T_DOCUMENTS;
        } else if (T_BLOB.equals(type)) {
            return T_BLOBS;
        } else {
            return "object";
        }
    }
}