protected void recursiveParse(Node node) { // Ignore null nodes or those we have seen before! if (node == null || parsedNodes.contains(node)) { return; } // Script level nodes have global list of function definitions. // Parse inner tokens for function definitions here. if (node instanceof ScriptOrFnNode) { ScriptOrFnNode sofn = (ScriptOrFnNode) node; int count = sofn.getFunctionCount() - 1; while (count > -1) { FunctionNode fn = sofn.getFunctionNode(count); recursiveParse(fn); count--; } } // Parse children from left to right. recursiveParse(node.getFirstChild()); recursiveParse(node.getLastChild()); // Parse this node and then move along to next parseNode(node); parsedNodes.add(node); recursiveParse(node.getNext()); }
// Try to find module paths expression in djConfig object literal. Merge results // into existing module paths map. protected void parseModulePathsExpr(Node objectLiteral) { if (objectLiteral.getType() == Token.OBJECTLIT) { Node modulePathsLiteral = findLiteralValue(objectLiteral, "modulePaths"); if (modulePathsLiteral != null && modulePathsLiteral.getType() == Token.OBJECTLIT) { // Put out all String module path values and merge into existing map. Map<String, String> modulePaths = findLiteralStringValues(modulePathsLiteral); this.modulePaths.putAll(modulePaths); } } }
protected void parseNode(Node node) { switch (node.getType()) { // Function call token case Token.CALL: if (isDojoCall(node)) { // We are looking for dojo.some_function(...) calls, this means first // child of function call is property resolution of function in the // dojo object. Node propertyResolution = node.getFirstChild(); if (propertyResolution != null) { // Find property label accessing object String propertyName = functionPropertyName(propertyResolution); // Single argument module functions, put out value and store in appropriate list. if ("require".equals(propertyName) || "provide".equals(propertyName) || "declare".equals(propertyName)) { String firstArg = functionCallArgument(propertyResolution, 0); List<String> moduleList = moduleLists.get(propertyName); if (!moduleList.contains(firstArg)) { moduleList.add(firstArg); } } else if ("registerModulePath".equals(propertyName)) { // Extract module identifier and module path, first two arguments to // dojo.registerModulePath(); String modulePrefix = functionCallArgument(propertyResolution, 0); String modulePath = functionCallArgument(propertyResolution, 1); modulePaths.put(modulePrefix, modulePath); } } } break; // Global identifier token, check for djConfig assignment, djConfig = {...} case Token.SETNAME: // LHS is global name binding. Node bindName = node.getFirstChild(); // LHS should be djConfig global assignment. if (bindName.getType() == Token.BINDNAME && "djConfig".equals(bindName.getString())) { // RHS of statement should be literal assignment. Node assignmentExpr = node.getLastChild(); parseModulePathsExpr(assignmentExpr); } break; // Local identifier token, check for djConfig assignment, var djConfig = {...} case Token.NAME: // LHS is name token if ("djConfig".equals(node.getString())) { // Check for object literal assignment Node assignmentExpr = node.getFirstChild(); parseModulePathsExpr(assignmentExpr); } break; } }
/** * Return the Constant Expression referred by the total constants. * * @param child * @return */ public static ConstantExpression getConstantExpression(Node child) { if (child.getFirstChild().getType() == Token.NAME && child.getFirstChild().getString().equalsIgnoreCase(TOTAL) && child.getLastChild().getType() == Token.STRING) { String property = child.getLastChild().getString(); if (CURRENT_GROUP.equalsIgnoreCase(property) || OVERALL.equalsIgnoreCase(property)) return new ConstantExpression(property.toUpperCase()); if (NO_FILTER.equalsIgnoreCase(property)) return new ConstantExpression(); } return null; }
protected boolean isDojoCall(Node functionCall) { boolean isDojoCall = false; Node propertyLookup = functionCall.getFirstChild(); // Check node is a property name lookup from the "dojo" object. // Property Name Lookup -> Source, Property if (propertyLookup.getType() == Token.GETPROP && propertyLookup.getFirstChild().getType() == Token.NAME) { String functionName = propertyLookup.getFirstChild().getString(); if ("dojo".equals(functionName)) { isDojoCall = true; } } return isDojoCall; }
// Given child nodes for a function call, object ref and arguments, return // the string value for the first argument. Ignore any complicated resolution. protected String functionCallArgument(Node functionCallNodes, int argIndex) { String fnCallArg = null; // Traverse argument nodes until we reach desired one. Node argument = functionCallNodes.getNext(); while (argument != null && argIndex > 0) { argument = argument.getNext(); argIndex--; } ; // Check we have a simple type value. if (argument != null && argument.getType() == Token.STRING) { fnCallArg = argument.getString(); } return fnCallArg; }
// Find an object literal's value for a given key. If not found, // return null. protected Node findLiteralValue(Node objLiteral, String key) { Node literalValueNode = null; // Get list of all properties Object[] propertyList = (Object[]) objLiteral.getProp(Token.EQ); // Iterate through key/values looking for a match if (propertyList != null) { Node propertyValue = objLiteral.getFirstChild(); for (int index = 0; index < propertyList.length; index++) { if (key.equals(propertyList[index])) { literalValueNode = propertyValue; break; } propertyValue = propertyValue.getNext(); } } return literalValueNode; }
protected Map<String, String> findLiteralStringValues(Node objLiteral) { Map<String, String> literalValues = new HashMap<String, String>(); // Find object literal keys from property list. Object[] propertyList = (Object[]) objLiteral.getProp(Token.EQ); // Iterate through each literal key, extracting associated value. Ignore // any non-string values, we aren't attempting inner-resolution of references. if (propertyList != null) { int numArgs = 0; // Child nodes are linked list of object values Node propertyValue = objLiteral.getFirstChild(); while (numArgs < propertyList.length) { // Convert key/value pairs into a handy format if (propertyValue.getType() == Token.STRING) { literalValues.put((String) propertyList[numArgs], propertyValue.getString()); } propertyValue = propertyValue.getNext(); numArgs++; } } return literalValues; }
// Extract property name accessed. Will have two child nodes, // object reference and property name. Ignore any further // variable resolution. protected String functionPropertyName(Node propertyLookup) { String propertyName = null; // First argument is an object reference that lookup should be // performed against. Node objectReference = propertyLookup.getFirstChild(); if (objectReference != null) { // Find property identifier we are retrieving... Node propertyNameNode = objectReference.getNext(); // Only handle explicit string properties, we cannot resolve variables. if (propertyNameNode != null && propertyNameNode.getType() == Token.STRING) { propertyName = propertyNameNode.getString(); } } return propertyName; }