com.github.jknack.amd4j.DependencyCollector.java Source code

Java tutorial

Introduction

Here is the source code for com.github.jknack.amd4j.DependencyCollector.java

Source

/**
 * Copyright (c) 2013 Edgar Espina
 *
 * This file is part of amd4j (https://github.com/jknack/amd4j)
 *
 * 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 com.github.jknack.amd4j;

import static org.apache.commons.io.FilenameUtils.getExtension;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.mozilla.javascript.Parser;
import org.mozilla.javascript.Token;
import org.mozilla.javascript.ast.ArrayLiteral;
import org.mozilla.javascript.ast.AstNode;
import org.mozilla.javascript.ast.AstRoot;
import org.mozilla.javascript.ast.FunctionCall;
import org.mozilla.javascript.ast.Name;
import org.mozilla.javascript.ast.NodeVisitor;
import org.mozilla.javascript.ast.StringLiteral;

/**
 * Collect module's dependencies.
 *
 * @author edgar.espina
 * @since 0.1.0
 */
final class DependencyCollector {

    /**
     * Not allowed.
     */
    private DependencyCollector() {
    }

    /**
     * Collect all the dependencies for the given module.
     *
     * @param config A configuration options.
     * @param module An AMD module.
     * @return A dependency set.
     */
    public static Set<String> collect(final Config config, final Module module) {
        // empty module?
        if (module.content.length() == 0) {
            return Collections.emptySet();
        }
        // just parse *.js files
        if (!"js".equals(getExtension(module.uri.getPath()))) {
            return Collections.emptySet();
        }

        return new NodeVisitor() {
            private Set<String> dependencies = new LinkedHashSet<String>();

            /**
             * Collect all the dependencies.
             *
             * @return A dependency set.
             */
            public Set<String> collect() {
                Parser parser = new Parser();
                AstRoot node = parser.parse(module.content.toString(), module.name, 1);
                node.visit(this);
                // check shim configuration
                Shim shim = config.getShim(module.name);
                if (shim != null) {
                    // add dependencies
                    if (shim.dependencies() != null) {
                        dependencies.addAll(shim.dependencies());
                    }
                }
                return dependencies;
            }

            @Override
            public boolean visit(final AstNode node) {
                int type = node.getType();
                switch (type) {
                case Token.CALL:
                    return visit((FunctionCall) node);
                default:
                    return true;
                }
            }

            /**
             * Find out "define" and "require" function calls.
             *
             * @param node The function call node.
             * @return True, to keep walking.
             */
            public boolean visit(final FunctionCall node) {
                AstNode target = node.getTarget();
                if (target instanceof Name) {
                    String name = ((Name) target).getIdentifier();
                    if ("define".equals(name)) {
                        visitDependencies(node);
                    } else if ("require".equals(name)) {
                        int depth = node.getParent().depth() - 1;
                        if (config.isFindNestedDependencies() || depth == 0) {
                            visitDependencies(node);
                        }
                    }
                }
                return true;
            }

            /**
             * Report module's dependencies.
             *
             * @param node The function's call.
             */
            private void visitDependencies(final FunctionCall node) {
                List<AstNode> arguments = node.getArguments();
                for (AstNode arg : arguments) {
                    if (arg instanceof ArrayLiteral) {
                        // found!
                        ArrayLiteral array = (ArrayLiteral) arg;
                        List<AstNode> dependencyList = array.getElements();
                        for (AstNode dependencyNode : dependencyList) {
                            String dependency = ((StringLiteral) dependencyNode).getValue();
                            String[] dependencies = StringUtils.split(dependency, "!");
                            if (dependencies.length > 1) {
                                this.dependencies.add(dependencies[0]);
                            }
                            this.dependencies.add(dependency);
                        }
                        break;
                    }
                }
            }
        }.collect();
    }
}