/* license-start * * Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>. * * This program 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 version 3. * * This program 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, at <http://www.gnu.org/licenses/>. * * Contributors: * Crispico - Initial API and implementation * * license-end */ package org.flowerplatform.codesync.code.javascript.parser; import java.util.regex.Pattern; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.impl.ResourceImpl; import org.flowerplatform.codesync.code.javascript.regex_ast.RegExAstNode; import org.flowerplatform.codesync.code.javascript.regex_ast.RegExAstFactory; import org.flowerplatform.codesync.code.javascript.regex_ast.RegExAstNodeParameter; import org.flowerplatform.common.regex.RegexConfiguration; import org.flowerplatform.common.regex.RegexProcessingSession; import org.flowerplatform.common.regex.RegexUtil; import org.flowerplatform.common.regex.RegexWithAction; import org.flowerplatform.editor.EditorPlugin; import org.flowerplatform.editor.file.IFileAccessController; /** * @author Mariana Gheorghe */ public class Parser { public static final String JS_FILE = "javaScriptFile"; public static final String HTML_FILE = "htmlFile"; ////////////////////////////////// // HTML ////////////////////////////////// public static final String HTML_CHILDREN_INSERT_POINT = "htmlChildrenInsertPoint"; public static final String HTML_CHILDREN_INSERT_POINT_REGEX = "<!-- children-insert-point (.*?) -->"; public static final String HTML_TABLE = "table"; public static final String HTML_TABLE_REGEX = "<!-- template table -->\\s*<table .*?id=\\s*\"(.*?)\">\\s*<tr .*?id=\\s*\"(.*?)\""; public static final String HTML_TABLE_END = "htmlTableEnd"; public static final String HTML_TABLE_END_REGEX = "</table>"; public static final String HTML_TABLE_HEADER_ENTRY = "tableHeaderEntry"; public static final String HTML_TABLE_HEADER_ENTRY_REGEX = "<th>(.*?)</th>"; public static final String HTML_TABLE_ITEM = "tableItem"; public static final String HTML_TABLE_ITEM_REGEX = "<!-- template tableItem -->"; public static final String HTML_TABLE_ITEM_URL = "tableItemUrl"; public static final String HTML_TABLE_ITEM_URL_REGEX = "<td><a .*?href=\"#(.+)/<.*?"; public static final String HTML_TABLE_ITEM_ENTRY = "tableItemEntry"; public static final String HTML_TABLE_ITEM_ENTRY_REGEX = "<td>.*?<%= (.*?) %>.*?</td>"; public static final String HTML_FORM = "form"; public static final String HTML_FORM_REGEX = "<!-- template form -->.*?<table"; public static final String HTML_FORM_END = "htmlFormEnd"; public static final String HTML_FORM_END_REGEX = "</table>"; public static final String HTML_FORM_ID_SUFFIX_REGEX = "type=\"button\"\\s*?id=\"edit-(.*?)\""; public static final String HTML_FORM_ITEM = "formItem"; public static final String HTML_FORM_ITEM_REGEX = "<tr.*?<td>(.*?):</td>.*?<td .*?<%= (.*?) %>.*?</tr>"; ////////////////////////////////// // JS ////////////////////////////////// public static final String JS_CHILDREN_INSERT_POINT = "jsChildrenInsertPoint"; public static final String JS_CHILDREN_INSERT_POINT_REGEX = "// children-insert-point (\\S*)"; public static final String JS_BACKBONE_CLASS = "backboneClass"; public static final String JS_BACKBONE_CLASS_REGEX = "// template backboneClass"; public static final String JS_BACKBONE_SUPER_CLASS = "jsBackboneClassSuperClass"; public static final String JS_BACKBONE_SUPER_CLASS_REGEX = "return\\s*+(.*?).extend\\s*\\("; public static final String JS_REQUIRE_ENTRY = "requireEntry"; public static final String JS_REQUIRE_ENTRY_CATEGORY = "Require"; public static final String JS_REQUIRE_ENTRY_REGEX = "var\\s*?(\\S*?)\\s*?=\\s*?require\\('(.*?)'\\)\\s*?;"; public static final String JS_BACKBONE_CLASS_MEMBER = "classMember"; public static final String JS_OPERATION = "javaScriptOperation"; public static final String JS_OPERATION_CATEGORY = "Operation"; public static final String JS_OPERATION_REGEX = "(\\S*?)\\s*?:\\s*?function\\s*?\\((.*?)\\)\\s*?\\{"; public static final String JS_ATTRIBUTE = "javaScriptAttribute"; public static final String JS_ATTRIBUTE_CATEGORY = "Attribute"; public static final String JS_ATTRIBUTE_REGEX = "(\\w+)\\s*+:\\s*([\\S&&[^,/]]+)"; public static final String JS_EVENTS_ATTRIBUTE = "eventsAttribute"; public static final String JS_EVENTS_ATTRIBUTE_CATEGORY = "Events"; public static final String JS_EVENTS_ATTRIBUTE_REGEX = "(events)\\s*:\\s*\\{"; public static final String JS_EVENTS_ATTRIBUTE_ENTRY = "eventsAttributeEntry"; public static final String JS_EVENTS_ATTRIBUTE_ENTRY_REGEX = "\"(\\S+?)\\s*(\\S*?)\"\\s*:\\s*\"(\\S+?)\""; public static final String JS_ROUTES_ATTRIBUTE = "routesAttribute"; public static final String JS_ROUTES_ATTRIBUTE_CATEGORY = "Routes"; public static final String JS_ROUTES_ATTRIBUTE_REGEX = "(routes)\\s*:\\s*\\{"; public static final String JS_ROUTES_ATTRIBUTE_ENTRY = "routesAttributeEntry"; public static final String JS_ROUTES_ATTRIBUTE_ENTRY_REGEX = "\"(\\S+?)\"\\s*:\\s*\"(\\S+?)\""; public static final String NAME = "name"; public static RegexEngineState[] statesStack = new RegexEngineState[100]; public static RegexEngineState currentState; protected String input; protected RegExAstNode jsDoc; public RegExAstNode parse(Object file) { IFileAccessController fileAccessController = EditorPlugin.getInstance().getFileAccessController(); input = fileAccessController.readFileToString(file); RegExAstNode root = createRegExAstNode(fileAccessController.getPath(file).endsWith(".js") ? JS_FILE : HTML_FILE, NAME, false, 0, 0); addParameter(root, NAME, fileAccessController.getName(file).substring(0, fileAccessController.getName(file).indexOf(".")), 0, 0); RegexConfiguration config = new RegexConfiguration(); if (fileAccessController.getPath(file).endsWith(".js")) { buildJsConfig(config); } else { buildHtmlConfig(config); } RegexProcessingSession session = config.startSession(input); enterState(session, fileAccessController.getPath(file).endsWith(".js") ? JS_FILE : HTML_FILE, root, 0); try { while (session.find()) {} } catch (Exception e) { throw new RuntimeException(e); } // print("", root); // adding to resource to avoid UNDEFINED values during sync // see EObjectModelAdapter.getValueFeatureValue() Resource resource = new ResourceImpl(); resource.getContents().add(root); return root; } protected void buildHtmlConfig(RegexConfiguration config) { config .add(new RegexWithAction(HTML_CHILDREN_INSERT_POINT, HTML_CHILDREN_INSERT_POINT_REGEX) { @Override public void executeAction(RegexProcessingSession session) { currentState.node.getChildrenInsertPoints().put(session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex())); } }) .add(new RegexWithAction(HTML_TABLE, HTML_TABLE_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (currentState.category.equals(HTML_FILE)) { currentState.node.setType(HTML_TABLE); addParameter(currentState.node, "tableId", session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); addParameter(currentState.node, "headerRowId", session.getCurrentSubMatchesForCurrentRegex()[1], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 2), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 2)); currentState.node.setOffset(session.getMatcher().start(session.getCurrentMatchGroupIndex())); enterState(session, HTML_TABLE, currentState.node, 1); } } }) .add(new RegexWithAction(HTML_TABLE_END, HTML_TABLE_END_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (HTML_TABLE.equals(currentState.node.getType()) || HTML_FORM.equals(currentState.node.getType())) { exitState(session); } } }) .add(new RegexWithAction(HTML_TABLE_HEADER_ENTRY, HTML_TABLE_HEADER_ENTRY_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (HTML_TABLE.equals(currentState.node.getType())) { RegExAstNode entry = createRegExAstNode(HTML_TABLE_HEADER_ENTRY, "title", false, session.getMatcher().start(session.getCurrentMatchGroupIndex()), session.getMatcher().end(session.getCurrentMatchGroupIndex())); entry.setType(HTML_TABLE_HEADER_ENTRY); entry.setNextSiblingInsertPoint(session.getMatcher().end(session.getCurrentMatchGroupIndex())); addParameter(entry, "title", session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); currentState.node.getChildren().add(entry); } } }) .add(new RegexWithAction(HTML_TABLE_ITEM, HTML_TABLE_ITEM_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (currentState.category.equals(HTML_FILE)) { currentState.node.setType(HTML_TABLE_ITEM); currentState.node.setOffset(session.getMatcher().start(session.getCurrentMatchGroupIndex())); enterState(session, HTML_TABLE_ITEM, currentState.node, 1); } } }) .add(new RegexWithAction(HTML_TABLE_ITEM_URL, HTML_TABLE_ITEM_URL_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (HTML_TABLE_ITEM.equals(currentState.node.getType())) { addParameter(currentState.node, "itemUrl", session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); exitState(session); } } }) .add(new RegexWithAction(HTML_TABLE_ITEM_ENTRY, HTML_TABLE_ITEM_ENTRY_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (HTML_TABLE_ITEM.equals(currentState.node.getType())) { RegExAstNode entry = createRegExAstNode(HTML_TABLE_ITEM_ENTRY, "valueExpression", false, session.getMatcher().start(session.getCurrentMatchGroupIndex()), session.getMatcher().end(session.getCurrentMatchGroupIndex())); entry.setType(HTML_TABLE_ITEM_ENTRY); entry.setNextSiblingInsertPoint(session.getMatcher().end(session.getCurrentMatchGroupIndex())); addParameter(entry, "valueExpression", session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); currentState.node.getChildren().add(entry); } } }) .add(new RegexWithAction(HTML_FORM, HTML_FORM_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (currentState.category.equals(HTML_FILE)) { currentState.node.setType(HTML_FORM); currentState.node.setOffset(session.getMatcher().start(session.getCurrentMatchGroupIndex())); enterState(session, HTML_FORM, currentState.node, 1); } } }) .add(new RegexWithAction(HTML_FORM, HTML_FORM_ID_SUFFIX_REGEX) { @Override public void executeAction(RegexProcessingSession session) { addParameter(currentState.node, "idSuffix", session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); } }) .add(new RegexWithAction(HTML_FORM_ITEM, HTML_FORM_ITEM_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (HTML_FORM.equals(currentState.node.getType())) { RegExAstNode entry = createRegExAstNode(HTML_FORM_ITEM, "title", false, session.getMatcher().start(session.getCurrentMatchGroupIndex()), session.getMatcher().end(session.getCurrentMatchGroupIndex())); entry.setType(HTML_FORM_ITEM); entry.setNextSiblingInsertPoint(session.getMatcher().end(session.getCurrentMatchGroupIndex())); addParameter(entry, "title", session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); addParameter(entry, "valueExpression", session.getCurrentSubMatchesForCurrentRegex()[1], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 2), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 2)); currentState.node.getChildren().add(entry); } } }) .compile(Pattern.DOTALL); } protected void buildJsConfig(RegexConfiguration config) { config .add(new RegexWithAction(JS_CHILDREN_INSERT_POINT, JS_CHILDREN_INSERT_POINT_REGEX) { @Override public void executeAction(RegexProcessingSession session) { currentState.node.getChildrenInsertPoints().put(session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex())); } }) .add(new RegexWithAction(JS_BACKBONE_CLASS, JS_BACKBONE_CLASS_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (currentState.category.equals(JS_FILE)) { currentState.node.setType(JS_BACKBONE_CLASS); attachJsDoc(currentState.node); enterState(session, JS_BACKBONE_CLASS, currentState.node, 1); } } }) .add(new RegexWithAction.IfFindThisSkip("multiline commment", RegexUtil.MULTI_LINE_COMMENT)) .add(new RegexWithAction.IfFindThisSkip("single line comment", RegexUtil.SINGLE_LINE_COMMENT)) .add(new RegexWithAction("closing bracket", "[\\}\\]\\)]") { @Override public void executeAction(RegexProcessingSession session) { exitState(session); } }) .add(new RegexWithAction("opening bracket", "[\\{\\[\\(]") { @Override public void executeAction(RegexProcessingSession session) { session.currentNestingLevel++; } }) .add(new RegexWithAction(JS_BACKBONE_SUPER_CLASS, JS_BACKBONE_SUPER_CLASS_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (JS_BACKBONE_CLASS.equals(currentState.node.getType())) { addParameter(currentState.node, "superClass", session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); } } }) .add(new RegexWithAction(JS_REQUIRE_ENTRY, JS_REQUIRE_ENTRY_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (JS_BACKBONE_CLASS.equals(currentState.node.getType())) { RegExAstNode requireEntry = addToCategory(JS_REQUIRE_ENTRY_CATEGORY, currentState.node, "varName", JS_REQUIRE_ENTRY, session.getMatcher().start(session.getCurrentMatchGroupIndex()), session.getMatcher().end(session.getCurrentMatchGroupIndex())); requireEntry.setType(JS_REQUIRE_ENTRY); requireEntry.setNextSiblingInsertPoint(session.getMatcher().end(session.getCurrentMatchGroupIndex())); addParameter(requireEntry, "varName", session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); addParameter(requireEntry, "dependencyPath", session.getCurrentSubMatchesForCurrentRegex()[1], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 2), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 2)); attachJsDoc(requireEntry); } } }) .add(new RegexWithAction(JS_OPERATION, JS_OPERATION_REGEX) { @Override public void executeAction(RegexProcessingSession session) { RegExAstNode function = addToCategory(JS_OPERATION_CATEGORY, currentState.node, NAME, JS_OPERATION, session.getMatcher().start(), 0); function.setType(JS_OPERATION); addParameter(function, NAME, session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); addParameter(function, "parameters", session.getCurrentSubMatchesForCurrentRegex()[1], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 2), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 2)); attachJsDoc(function); enterState(session, JS_OPERATION, function, 1); } }) .add(new RegexWithAction(JS_EVENTS_ATTRIBUTE, JS_EVENTS_ATTRIBUTE_REGEX) { @Override public void executeAction(RegexProcessingSession session) { RegExAstNode events = addToCategory(JS_ATTRIBUTE_CATEGORY, currentState.node, NAME, JS_EVENTS_ATTRIBUTE, session.getMatcher().start(), session.getMatcher().end()); addParameter(events, "name", session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); events.setType(JS_EVENTS_ATTRIBUTE); enterState(session, JS_EVENTS_ATTRIBUTE, currentState.node, 1); attachJsDoc(events); } }) .add(new RegexWithAction(JS_ROUTES_ATTRIBUTE, JS_ROUTES_ATTRIBUTE_REGEX) { @Override public void executeAction(RegexProcessingSession session) { RegExAstNode routes = addToCategory(JS_ATTRIBUTE_CATEGORY, currentState.node, NAME, JS_ROUTES_ATTRIBUTE, session.getMatcher().start(), session.getMatcher().end()); addParameter(routes, "name", session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); routes.setType(JS_ROUTES_ATTRIBUTE); enterState(session, JS_ROUTES_ATTRIBUTE, currentState.node, 1); attachJsDoc(routes); } }) .add(new RegexWithAction(JS_ATTRIBUTE, JS_ATTRIBUTE_REGEX) { @Override public void executeAction(RegexProcessingSession session) { RegExAstNode attr = addToCategory(JS_ATTRIBUTE_CATEGORY, currentState.node, NAME, JS_ATTRIBUTE, session.getMatcher().start(), session.getMatcher().end()); attr.setType(JS_ATTRIBUTE); attr.setNextSiblingInsertPoint(attr.getOffset() + attr.getLength()); addParameter(attr, NAME, session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); addParameter(attr, "defaultValue", session.getCurrentSubMatchesForCurrentRegex()[1], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 2), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 2)); attachJsDoc(attr); // enterState(session, JS_ATTRIBUTE, attr, 1); } }) .add(new RegexWithAction(JS_EVENTS_ATTRIBUTE_ENTRY, JS_EVENTS_ATTRIBUTE_ENTRY_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (currentState.category.equals(JS_EVENTS_ATTRIBUTE)) { RegExAstNode event = addToCategory(JS_EVENTS_ATTRIBUTE_CATEGORY, currentState.node, "event", JS_EVENTS_ATTRIBUTE_ENTRY, session.getMatcher().start(), session.getMatcher().end()); event.setType(JS_EVENTS_ATTRIBUTE_ENTRY); addParameter(event, "event", session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); addParameter(event, "selector", session.getCurrentSubMatchesForCurrentRegex()[1], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 2), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 2)); addParameter(event, "handler", session.getCurrentSubMatchesForCurrentRegex()[2], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 3), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 3)); } } }) .add(new RegexWithAction(JS_ROUTES_ATTRIBUTE_ENTRY, JS_ROUTES_ATTRIBUTE_ENTRY_REGEX) { @Override public void executeAction(RegexProcessingSession session) { if (currentState.category.equals(JS_ROUTES_ATTRIBUTE)) { RegExAstNode route = addToCategory(JS_ROUTES_ATTRIBUTE_CATEGORY, currentState.node, "path", JS_ROUTES_ATTRIBUTE_ENTRY, session.getMatcher().start(), session.getMatcher().end()); route.setType(JS_ROUTES_ATTRIBUTE_ENTRY); addParameter(route, "path", session.getCurrentSubMatchesForCurrentRegex()[0], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 1), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 1)); addParameter(route, "function", session.getCurrentSubMatchesForCurrentRegex()[1], session.getMatcher().start(session.getCurrentMatchGroupIndex() + 2), session.getMatcher().end(session.getCurrentMatchGroupIndex() + 2)); } } }) .compile(Pattern.DOTALL); } protected static void enterState(RegexProcessingSession session, String state, RegExAstNode node, int increment) { session.currentNestingLevel += increment; currentState = new RegexEngineState(state, node); statesStack[session.currentNestingLevel] = currentState; } protected static void exitState(RegexProcessingSession session) { if (session.currentNestingLevel > 0) { if (statesStack[session.currentNestingLevel--] != null) { currentState.node.setLength(session.getMatcher().end() - currentState.node.getOffset()); currentState.node.setNextSiblingInsertPoint(session.getMatcher().end()); statesStack[session.currentNestingLevel + 1] = null; int index = session.currentNestingLevel; while (statesStack[index] == null) { index--; } currentState = statesStack[index]; } } } protected static RegExAstNode createRegExAstNode(String type, String keyParameter, boolean isCategoryNode, int start, int end) { RegExAstNode node = RegExAstFactory.eINSTANCE.createRegExAstNode(); node.setType(type); node.setKeyParameter(keyParameter); node.setOffset(start); node.setLength(end - start); return node; } protected RegExAstNode addToCategory(String category, RegExAstNode parent, String keyParameter, String type, int start, int end) { RegExAstNode node = createRegExAstNode(type, keyParameter, false, start, end); // getCategory(parent, category).getChildren().add(node); parent.getChildren().add(node); return node; } protected void addParameter(RegExAstNode node, String name, String value, int start, int end) { RegExAstNodeParameter parameter = RegExAstFactory.eINSTANCE.createRegExAstNodeParameter(); parameter.setName(name); parameter.setValue(value); parameter.setValue(value); parameter.setOffset(start); parameter.setLength(end - start); node.getParameters().add(parameter); } private String getParameterValue(RegExAstNode node, String name) { for (RegExAstNodeParameter parameter : node.getParameters()) { if (parameter.getName().equals(name)) { return parameter.getValue(); } } return ""; } protected void attachJsDoc(RegExAstNode node) { if (jsDoc != null) { node.getChildren().add(jsDoc); jsDoc = null; } } private void print(String indent, RegExAstNode node) { String label = new String(node.getType() + " "); label += getParameterValue(node, node.getKeyParameter()); if (node.getOffset() != 0 || node.getLength() != 0) { label += " (" + node.getOffset() + ", " + node.getLength() + ") "; } label += ": "; for (RegExAstNodeParameter parameter : node.getParameters()) { if (parameter.getOffset() != 0 || parameter.getLength() != 0) { label += String.format("(%s = %s) (%s, %s), ", parameter.getName(), parameter.getValue().replaceAll("\n", ""), parameter.getOffset(), parameter.getLength()); } else { label += String.format("(%s = %s), ", parameter.getName(), parameter.getValue().replace("\n", "")); } } label = label.substring(0, label.length() - 2); System.out.println(indent + label); for (RegExAstNode child : node.getChildren()) { print(indent + " ", child); } } }