+++ /dev/null
-/*--------------------------------------------------------------------------\r
- * Copyright 2009 Taro L. Saito\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- *--------------------------------------------------------------------------*/\r
-//--------------------------------------\r
-// XerialJ\r
-//\r
-// SilkParser.java\r
-// Since: Jun 1, 2009 2:45:39 PM\r
-//\r
-// $URL: http://www.xerial.org/svn/project/XerialJ/trunk/xerial-core/src/main/java/org/xerial/silk/SilkParser.java $\r
-// $Author: leo $\r
-//--------------------------------------\r
-package org.xerial.silk;\r
-\r
-import java.io.IOException;\r
-import java.io.InputStreamReader;\r
-import java.io.Reader;\r
-import java.lang.reflect.Field;\r
-import java.net.URL;\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.TreeMap;\r
-import java.util.regex.Pattern;\r
-\r
-import org.xerial.core.XerialErrorCode;\r
-import org.xerial.core.XerialException;\r
-import org.xerial.json.JSONArray;\r
-import org.xerial.json.JSONEvent;\r
-import org.xerial.json.JSONException;\r
-import org.xerial.json.JSONObject;\r
-import org.xerial.json.JSONPullParser;\r
-import org.xerial.json.JSONUtil;\r
-import org.xerial.json.JSONValue;\r
-import org.xerial.json.JSONValueType;\r
-import org.xerial.silk.impl.SilkDataLine;\r
-import org.xerial.silk.impl.SilkFunction;\r
-import org.xerial.silk.impl.SilkFunctionArg;\r
-import org.xerial.silk.impl.SilkJSONValue;\r
-import org.xerial.silk.impl.SilkNode;\r
-import org.xerial.silk.impl.SilkNodeOccurrence;\r
-import org.xerial.silk.impl.SilkValue;\r
-import org.xerial.silk.plugin.SilkFunctionArgument;\r
-import org.xerial.silk.plugin.SilkFunctionPlugin;\r
-import org.xerial.util.FileResource;\r
-import org.xerial.util.StringUtil;\r
-import org.xerial.util.bean.TypeInfo;\r
-import org.xerial.util.log.Logger;\r
-import org.xerial.util.reflect.ReflectionUtil;\r
-import org.xerial.util.tree.TreeEventHandler;\r
-import org.xerial.util.tree.TreeParser;\r
-import org.xerial.util.xml.impl.TreeEventQueue;\r
-\r
-/**\r
- * Push-style Silk format parser\r
- * \r
- * @author leo\r
- * \r
- */\r
-public class SilkParser implements SilkEventHandler, TreeParser {\r
- private static Logger _logger = Logger.getLogger(SilkParser.class);\r
-\r
- private final SilkLineParser parser;\r
- private final SilkEnv parseContext;\r
- private final SilkParserConfig config;\r
-\r
- private TreeEventQueue eventQueue = new TreeEventQueue();\r
- private long numReadLine = 0;\r
-\r
- /**\r
- * Creates a new reader with the specified reader\r
- * \r
- * @param input\r
- * @throws IOException\r
- */\r
- public SilkParser(Reader input) throws IOException {\r
- this(input, SilkEnv.newEnv());\r
- }\r
-\r
- /**\r
- * Creates a new reader inherited the given environment\r
- * \r
- * @param input\r
- * @param env\r
- * @throws IOException\r
- */\r
- public SilkParser(Reader input, SilkEnv env) throws IOException {\r
- this(input, env, new SilkParserConfig());\r
- }\r
-\r
- /**\r
- * Create a new reader for reading the specified resource URL\r
- * \r
- * @param resourcePath\r
- * @throws IOException\r
- */\r
- public SilkParser(URL resourcePath) throws IOException {\r
- this(resourcePath, SilkEnv.newEnv());\r
- }\r
-\r
- public SilkParser(URL resourcePath, SilkParserConfig config) throws IOException {\r
- this(resourcePath, SilkEnv.newEnv(), config);\r
- }\r
-\r
- public SilkParser(URL resource, SilkEnv env) throws IOException {\r
- this(resource, env, new SilkParserConfig());\r
- }\r
-\r
- public SilkParser(URL resource, SilkEnv env, SilkParserConfig config) throws IOException {\r
- this(new InputStreamReader(resource.openStream()), SilkEnv.newEnv(env,\r
- getResourceBasePath(resource)), config);\r
- }\r
-\r
- public SilkParser(Reader input, SilkEnv env, SilkParserConfig config) throws IOException {\r
- this.config = config;\r
- if (config.numWorkers > 1)\r
- this.parser = new SilkLineFastParser(input);\r
- else\r
- this.parser = new SilkLinePushParser(input);\r
- this.parseContext = env;\r
- }\r
-\r
- static String getResourceBasePath(URL resource) {\r
- String path = resource.toExternalForm();\r
- int fileNamePos = path.lastIndexOf("/");\r
- String resourceBasePath = fileNamePos > 0 ? path.substring(0, fileNamePos) : null;\r
- return resourceBasePath;\r
- }\r
-\r
- /**\r
- * Concatenates the base path and the resource name\r
- * \r
- * @param resourceBasePath\r
- * @param resourceName\r
- * @return\r
- */\r
- static String getResourcePath(String resourceBasePath, String resourceName) {\r
- String resourcePath = resourceBasePath;\r
- if (!resourcePath.endsWith("/"))\r
- resourcePath += "/";\r
- resourcePath += resourceName;\r
- return resourcePath;\r
- }\r
-\r
- TreeEventHandler handler;\r
-\r
- public void parse(TreeEventHandler handler) throws Exception {\r
- this.handler = handler;\r
-\r
- handler.init();\r
- parser.parse(this);\r
- closeContextUpTo(parseContext.getIndentationOffset());\r
-\r
- handler.finish();\r
- }\r
-\r
- public void parseWithoutInitAndFinish(TreeEventHandler handler) throws Exception {\r
- this.handler = handler;\r
-\r
- parser.parse(this);\r
- closeContextUpTo(parseContext.getIndentationOffset());\r
- }\r
-\r
- public void handle(SilkEvent event) throws Exception {\r
- try {\r
- switch (event.getType()) {\r
- case DATA_LINE:\r
- parseDataLine(event);\r
- break;\r
- case NODE:\r
- SilkNode newContextNode = SilkNode.class.cast(event.getElement());\r
- openContext(newContextNode);\r
- break;\r
- case FUNCTION:\r
- SilkFunction function = SilkFunction.class.cast(event.getElement());\r
- evalFunction(function);\r
- break;\r
- case MULTILINE_ENTRY_SEPARATOR: // ==\r
- {\r
- SilkContext context = parseContext.peekLastContext();\r
- SilkNode schema = context.contextNode;\r
- if (parseContext.isAttributeOpen) {\r
- SilkNode attributeNode = schema.getChildNodes().get(\r
- parseContext.contextNodeAttributeCursor);\r
- leave(attributeNode.getName());\r
- }\r
- leave(schema.getName());\r
- // reset\r
- parseContext.contextNodeAttributeCursor = 0;\r
- parseContext.isAttributeOpen = false;\r
- break;\r
- }\r
- case MULTILINE_SEPARATOR: {\r
- SilkContext context = parseContext.peekLastContext();\r
- SilkNode schema = context.contextNode;\r
- if (parseContext.isAttributeOpen) {\r
- SilkNode attributeNode = schema.getChildNodes().get(\r
- parseContext.contextNodeAttributeCursor);\r
- leave(attributeNode.getName());\r
- }\r
- parseContext.contextNodeAttributeCursor++;\r
- parseContext.isAttributeOpen = false;\r
- break;\r
- }\r
- case BLANK_LINE:\r
- break;\r
- case END_OF_FILE:\r
- break;\r
- case PREAMBLE:\r
- break;\r
- case COMMENT_LINE:\r
- break;\r
- case UNKNOWN:\r
- default:\r
- _logger.warn(String.format("unknown event type (line=%d): %s", numReadLine, event));\r
- break;\r
- }\r
- }\r
- catch (XerialException e) {\r
- // When finding the parse error, only report warning message with the line number\r
- if (e.getErrorCode() == XerialErrorCode.PARSE_ERROR)\r
- _logger.warn(String.format("parse error at line=%d: %s", numReadLine, e));\r
- else\r
- throw e;\r
-\r
- }\r
-\r
- numReadLine++;\r
- }\r
-\r
- private static final Pattern tabSplit = Pattern.compile("\t");\r
- private static final Pattern commaSplit = Pattern.compile(",");\r
-\r
- private void parseDataLine(SilkEvent currentEvent) throws Exception {\r
- // pop the context stack until finding a node for stream data node occurrence\r
- while (!parseContext.isContextNodeStackEmpty()) {\r
- SilkContext context = parseContext.peekLastContext();\r
- SilkNode node = context.contextNode;\r
- if (!node.getOccurrence().isFollowedByStreamData()) {\r
- parseContext.popLastContext();\r
- if (context.isOpen)\r
- leave(node.getName());\r
- }\r
- else\r
- break;\r
- }\r
-\r
- if (parseContext.isContextNodeStackEmpty()) {\r
- // use default column names(c1, c2, ...) \r
- SilkDataLine line = SilkDataLine.class.cast(currentEvent.getElement());\r
- String[] columns = tabSplit.split(line.getTrimmedDataLine(), 0);\r
- int index = 1;\r
- visit("row", null);\r
- for (String each : columns) {\r
- String columnName = String.format("c%d", index++);\r
-\r
- // TODO use evalColumnData\r
- visit(columnName, each);\r
- leave(columnName);\r
- }\r
- leave("row");\r
- }\r
- else {\r
- SilkContext context = parseContext.peekLastContext();\r
- SilkNode schema = context.contextNode;\r
- SilkDataLine line = SilkDataLine.class.cast(currentEvent.getElement());\r
- switch (schema.getOccurrence()) {\r
- case SEQUENCE:\r
- text(line.getTrimmedDataLine());\r
- break;\r
- case SEQUENCE_WITH_NEWLINE:\r
- text(line.getDataLine() + StringUtil.NEW_LINE);\r
- break;\r
- case ZERO_OR_MORE:\r
- // CSV data\r
- {\r
- evalDatalineColumn(schema, line.getTrimmedDataLine());\r
- }\r
- break;\r
- case TABBED_SEQUENCE: {\r
- String[] columns = tabSplit.split(line.getTrimmedDataLine(), 0);\r
-\r
- int columnIndex = 0;\r
- visit(schema.getName(), schema.hasValue() ? schema.getValue().toString() : null);\r
- for (int i = 0; i < schema.getChildNodes().size(); i++) {\r
- SilkNode child = schema.getChildNodes().get(i);\r
- if (child.hasValue()) {\r
- // output the default value for the column \r
- evalDatalineColumn(child, child.getValue().toString());\r
- }\r
- else {\r
- if (columnIndex < columns.length) {\r
- String columnData = columns[columnIndex++].trim();\r
- if (columnData.length() > 0)\r
- evalDatalineColumn(child, columnData);\r
- }\r
- }\r
- }\r
- leave(schema.getName());\r
- break;\r
- }\r
- case MULTILINE_SEQUENCE: {\r
- int cursor = parseContext.contextNodeAttributeCursor;\r
-\r
- if (cursor >= schema.getChildNodes().size())\r
- break;\r
-\r
- SilkNode attributeNode = schema.getChildNodes().get(cursor);\r
- if (cursor == 0 && !parseContext.isAttributeOpen) {\r
- visit(schema.getName(), schema.hasValue() ? schema.getValue().toString() : null);\r
- }\r
- if (!parseContext.isAttributeOpen) {\r
- if (attributeNode.hasValue())\r
- visit(attributeNode.getName(), attributeNode.getValue().toString());\r
- else\r
- visit(attributeNode.getName(), null);\r
-\r
- parseContext.isAttributeOpen = true;\r
- }\r
- text(line.getTrimmedDataLine());\r
- break;\r
- }\r
- }\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Enqueues a visit event.\r
- * \r
- * @param nodeName\r
- * @param immediateNodeValue\r
- * @throws Exception\r
- */\r
- private void visit(String nodeName, String immediateNodeValue) throws Exception {\r
- handler.visitNode(nodeName, immediateNodeValue);\r
-\r
- }\r
-\r
- /**\r
- * Enqueues a leave event\r
- * \r
- * @param nodeName\r
- * @throws Exception\r
- */\r
- private void leave(String nodeName) throws Exception {\r
- handler.leaveNode(nodeName);\r
- }\r
-\r
- /**\r
- * Enqueues a text event\r
- * \r
- * @param textFragment\r
- * @throws XerialException\r
- */\r
- private void text(String textFragment) throws Exception {\r
- handler.text(parseContext.getContextNode().getName(), textFragment);\r
- }\r
-\r
- /**\r
- * Closed pre-opened contexts up to the specified indent level\r
- * \r
- * @param newIndentLevel\r
- * @throws Exception\r
- */\r
- private void closeContextUpTo(int newIndentLevel) throws Exception {\r
- while (!parseContext.isContextNodeStackEmpty()) {\r
- SilkContext context = parseContext.peekLastContext();\r
- SilkNode node = context.contextNode;\r
- if (node.getIndentLevel() >= newIndentLevel) {\r
- parseContext.popLastContext();\r
-\r
- if (parseContext.isAttributeOpen) {\r
- // close attribute \r
- SilkNode attribute = node.getChildNodes().get(\r
- parseContext.contextNodeAttributeCursor);\r
- leave(attribute.getName());\r
- leave(node.getName());\r
- parseContext.isAttributeOpen = false;\r
- }\r
-\r
- if (context.isOpen) {\r
- // close context\r
- String nodeName = node.getName();\r
- leave(nodeName);\r
- }\r
- }\r
- else\r
- return;\r
- }\r
- }\r
-\r
- /**\r
- * Opens a new context for the given node\r
- * \r
- * @param node\r
- * new context node\r
- * @param visitor\r
- * @throws Exception\r
- */\r
- private void openContext(SilkNode node) throws Exception {\r
- int indentLevel = node.getIndentLevel();\r
- if (indentLevel != SilkNode.NO_INDENT)\r
- indentLevel += parseContext.getIndentationOffset();\r
-\r
- closeContextUpTo(indentLevel);\r
- openContext_internal(node);\r
- }\r
-\r
- private void openContext_internal(SilkNode node) throws Exception {\r
- if (node.getName() == null) {\r
- // no name nodes must hierarchically organize attribute nodes\r
- for (SilkNode eachChild : node.getChildNodes()) {\r
- eachChild.setNodeIndent(node.getNodeIndent());\r
- openContext_internal(eachChild);\r
- }\r
- return;\r
- }\r
-\r
- SilkContext currentContext = new SilkContext(node, true);\r
- parseContext.pushContext(currentContext);\r
-\r
- SilkNodeOccurrence occurrence = node.getOccurrence();\r
- if (occurrence.isSchemaOnlyNode()) {\r
- currentContext.isOpen = false;\r
- // reset the attribute cursor\r
- parseContext.contextNodeAttributeCursor = 0;\r
- parseContext.isAttributeOpen = false;\r
- return; // do not invoke visit events\r
- }\r
-\r
- String nodeName = node.getName();\r
- SilkValue textValue = node.getValue();\r
-\r
- // process text values attached to the node\r
- if (textValue != null) {\r
- // When the text data is JSON, traverses the JSON data \r
- if (textValue.isJSON()) {\r
-\r
- SilkJSONValue jsonValue = SilkJSONValue.class.cast(textValue);\r
- if (jsonValue.isObject()) {\r
- visit(nodeName, null);\r
- JSONObject jsonObj = new JSONObject(jsonValue.getValue());\r
- walkJSONObject(jsonObj);\r
- }\r
- else {\r
- currentContext.isOpen = false;\r
- JSONArray jsonArray = new JSONArray(jsonValue.getValue());\r
- walkJSONAray(jsonArray, nodeName);\r
- }\r
- }\r
- else if (textValue.isFunction()) {\r
- // evaluate the function \r
- visit(nodeName, null);\r
- SilkFunction function = SilkFunction.class.cast(textValue);\r
- evalFunction(function);\r
-\r
- return;\r
- }\r
- else {\r
- // Simple text value will be reported as it is.\r
- visit(nodeName, textValue.toString());\r
- }\r
- }\r
- else {\r
- if (occurrence == SilkNodeOccurrence.ZERO_OR_MORE) {\r
- // CSV data\r
- return; // do not invoke visit events\r
- }\r
-\r
- // Report a visit event without text value\r
- visit(nodeName, null);\r
- }\r
-\r
- // Traverse attribute nodes having text values. If no text value is specified for these attributes, \r
- // they are schema elements for the following DATA_LINE. \r
- for (SilkNode eachChild : node.getChildNodes()) {\r
- if (eachChild.hasValue()) {\r
- openContext(eachChild);\r
- }\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Evaluate the function\r
- * \r
- * @param function\r
- * @throws Exception\r
- */\r
- private void evalFunction(SilkFunction function) throws Exception {\r
- SilkFunctionPlugin plugin = getPlugin(function.getName());\r
- if (plugin == null) {\r
- _logger.error(String.format("plugin %s not found", function.getName()));\r
- return;\r
- }\r
- // fill the function argument to the plugin instance\r
- populate(plugin, function.getArgumentList());\r
-\r
- // evaluate the function\r
- SilkEnv env = parseContext.newEnvFor(function);\r
- plugin.init(env);\r
- plugin.eval(env, handler);\r
- }\r
-\r
- /**\r
- * Has finished reading the stream?\r
- */\r
- private boolean hasFinished = false;\r
-\r
- private void walkMicroFormatRoot(SilkNode schemaNode, JSONArray value) throws Exception {\r
- // e.g., exon(start, name)\r
-\r
- if (schemaNode.hasManyOccurrences()) {\r
- if (schemaNode.hasChildren()) {\r
- // e.g., exon(start, name)*\r
- // multiple occurrences: [[start, end], [start, end], ... ] \r
- for (int i = 0; i < value.size(); i++) {\r
- JSONArray eachElement = value.getJSONArray(i);\r
- if (eachElement == null)\r
- continue;\r
-\r
- visit(schemaNode.getName(), null);\r
- int index = 0;\r
- for (SilkNode eachSubSchema : schemaNode.getChildNodes()) {\r
- walkMicroFormatElement(eachSubSchema, eachElement.get(index++));\r
- }\r
- leave(schemaNode.getName());\r
- }\r
- }\r
- else {\r
- // e.g. QV*: [20, 50, 50]\r
- for (int i = 0; i < value.size(); i++) {\r
- visit(schemaNode.getName(), value.get(i).toString());\r
- leave(schemaNode.getName());\r
- }\r
- }\r
- }\r
- else {\r
- // [e1, e2, ...]\r
- visit(schemaNode.getName(), null);\r
- int index = 0;\r
- if (schemaNode.getChildNodes().size() != value.size()) {\r
- throw new XerialException(XerialErrorCode.INVALID_INPUT, String.format(\r
- "data format doesn't match: schema=%s, value=%s", schemaNode, value));\r
- }\r
- for (SilkNode each : schemaNode.getChildNodes()) {\r
- walkMicroFormatElement(each, value.get(index++));\r
- }\r
- leave(schemaNode.getName());\r
- }\r
- }\r
-\r
- private void walkMicroFormatElement(SilkNode schemaNode, JSONValue value) throws Exception {\r
- if (schemaNode.hasChildren()) {\r
- JSONArray array = value.getJSONArray();\r
- if (array != null)\r
- walkMicroFormatRoot(schemaNode, array);\r
- else\r
- throw new XerialException(XerialErrorCode.INVALID_INPUT, String.format(\r
- "data format doesn't match: schema=%s, value=%s", schemaNode, value));\r
- }\r
- else {\r
- visit(schemaNode.getName(), value.toString());\r
- leave(schemaNode.getName());\r
- }\r
- }\r
-\r
- private void evalDatalineColumn(SilkNode node, String columnData) throws Exception {\r
- // 7600 lines/sec\r
-\r
- if (node.hasChildren()) {\r
- JSONArray array = new JSONArray(columnData);\r
- walkMicroFormatRoot(node, array);\r
- return;\r
- }\r
-\r
- switch (node.getOccurrence()) {\r
- case ONE:\r
- evalColumnData(node, columnData);\r
- break;\r
- case ZERO_OR_MORE:\r
- case ONE_OR_MORE:\r
- if (columnData.startsWith("[")) {\r
- // process JSON array\r
-\r
- // 500,080 nodes/s, 2.49 MB/s\r
- // 591,824 nodes/s, 2.95 MB/s (when using JSONPullParser.getValueAsText())\r
- new EvalJSON(columnData).parseJSONArray(node);\r
-\r
- // 431,197 nodes/s, 2.15 MB/s\r
- //JSONArray array = new JSONArray(columnData);\r
- //walkMicroFormatRoot(node, array);\r
- return;\r
- }\r
- else {\r
- String[] csv = commaSplit.split(columnData, 0);\r
- for (String each : csv) {\r
- String value = each.trim();\r
- evalColumnData(node, value);\r
- }\r
- return;\r
- }\r
- default:\r
- evalColumnData(node, columnData);\r
- return;\r
- }\r
-\r
- }\r
-\r
- private class EvalJSON {\r
- private final String json;\r
- private final JSONPullParser parser;\r
-\r
- EvalJSON(String json) {\r
- this.json = json;\r
- this.parser = new JSONPullParser(json);\r
- }\r
-\r
- void parseJSONArray(SilkNode schemaNode) throws Exception {\r
- if (schemaNode.hasManyOccurrences()) {\r
- JSONEvent e = parser.next();\r
- if (e != JSONEvent.StartArray)\r
- throw new XerialException(XerialErrorCode.PARSE_ERROR, "expected [ but " + e);\r
-\r
- if (schemaNode.hasChildren()) {\r
- // e.g., exon(start, name)*\r
- // multiple occurrences: [[start, end], [start, end], ... ]\r
-\r
- while ((e = parser.next()) != JSONEvent.EndArray) {\r
- JSONValue v = parser.getValue();\r
- JSONArray eachElement = v.getJSONArray();\r
- if (eachElement == null) {\r
- _logger.warn("not an JSONArray: " + v);\r
- continue;\r
- }\r
-\r
- visit(schemaNode.getName(), null);\r
- int index = 0;\r
- for (SilkNode eachSubSchema : schemaNode.getChildNodes()) {\r
- walkMicroFormatElement(eachSubSchema, eachElement.get(index++));\r
- }\r
- leave(schemaNode.getName());\r
-\r
- }\r
-\r
- }\r
- else {\r
- // e.g. QV*: [20, 50, 50]\r
- while ((e = parser.next()) != JSONEvent.EndArray) {\r
- visit(schemaNode.getName(), parser.getValueAsText());\r
- leave(schemaNode.getName());\r
- }\r
-\r
- }\r
- }\r
- else {\r
-\r
- // [e1, e2, ...]\r
- visit(schemaNode.getName(), null);\r
- int index = 0;\r
-\r
- for (SilkNode each : schemaNode.getChildNodes()) {\r
- if (parser.next() == JSONEvent.EndArray) {\r
- throw new XerialException(XerialErrorCode.INVALID_INPUT, String.format(\r
- "data format doesn't match: schema=%s, value=%s", schemaNode, json));\r
- }\r
-\r
- walkMicroFormatElement(each, parser.getValue());\r
- }\r
- leave(schemaNode.getName());\r
- }\r
-\r
- }\r
-\r
- void parseJSONArrayOf(SilkNode parent) throws XerialException {\r
- JSONEvent e = parser.next();\r
- if (e != JSONEvent.StartArray)\r
- throw new XerialException(XerialErrorCode.PARSE_ERROR, "expected [ but " + e);\r
-\r
- }\r
-\r
- }\r
-\r
- private void evalColumnData(SilkNode node, String columnData) throws Exception {\r
- try {\r
- if (node.hasChildren()) {\r
- // micro-data format\r
- JSONArray array = new JSONArray(columnData);\r
- walkMicroFormatRoot(node, array);\r
- return;\r
- }\r
-\r
- String dataType = node.getDataType();\r
- if (dataType != null && dataType.equalsIgnoreCase("json")) {\r
- JSONValue json = JSONUtil.parseJSON(columnData);\r
- if (json.getJSONObject() != null) {\r
- if (node.getName().equals("_")) // no name object\r
- {\r
- walkJSONValue(json, node.getName());\r
- }\r
- else {\r
- visit(node.getName(), null);\r
- walkJSONValue(json, node.getName());\r
- leave(node.getName());\r
- }\r
- }\r
- else\r
- walkJSONValue(json, node.getName());\r
- }\r
- else {\r
- visit(node.getName(), StringUtil.unquote(columnData));\r
- leave(node.getName());\r
- }\r
- }\r
- catch (JSONException e) {\r
- throw new XerialException(e.getErrorCode(), String.format("line=%d: %s", numReadLine, e\r
- .getMessage()));\r
- }\r
-\r
- }\r
-\r
- private void walkJSONAray(JSONArray jsonArray, String parentNodeName) throws Exception {\r
- for (JSONValue each : jsonArray) {\r
- walkJSONValue(each, parentNodeName);\r
- }\r
- }\r
-\r
- private void walkJSONObject(JSONObject jsonObj) throws Exception {\r
- for (String key : jsonObj.keys()) {\r
- JSONValue val = jsonObj.get(key);\r
- walkJSONValue(val, key);\r
- }\r
- }\r
-\r
- private void walkJSONValue(JSONValue value, String parentNodeName) throws Exception {\r
- JSONValueType type = value.getValueType();\r
- switch (type) {\r
- case Array:\r
- walkJSONAray(value.getJSONArray(), parentNodeName);\r
- break;\r
- case Object:\r
- walkJSONObject(value.getJSONObject());\r
- break;\r
- case Boolean:\r
- visit(parentNodeName, value.toString());\r
- leave(parentNodeName);\r
- break;\r
- case Double:\r
- visit(parentNodeName, value.toString());\r
- leave(parentNodeName);\r
- break;\r
- case Integer:\r
- visit(parentNodeName, value.toString());\r
- leave(parentNodeName);\r
- break;\r
- case Null:\r
- visit(parentNodeName, value.toString());\r
- leave(parentNodeName);\r
- break;\r
- case String:\r
- visit(parentNodeName, value.toString());\r
- leave(parentNodeName);\r
- break;\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Plugin holder\r
- */\r
- private static Map<String, Class<SilkFunctionPlugin>> pluginTable = null;\r
-\r
- /**\r
- * Get the plugin of the specified name\r
- * \r
- * @param name\r
- * plugin name\r
- * @return plugin instance or null if no corresponding plugin is found.\r
- */\r
- private SilkFunctionPlugin getPlugin(String name) {\r
- Class<SilkFunctionPlugin> pluginClass = getPluginTable().get(name);\r
- if (pluginClass == null)\r
- return null;\r
-\r
- try {\r
- SilkFunctionPlugin pluginInstance = pluginClass.newInstance();\r
- return pluginInstance;\r
- }\r
- catch (InstantiationException e) {\r
- _logger.warn(e);\r
- return null;\r
- }\r
- catch (IllegalAccessException e) {\r
- _logger.warn(e);\r
- return null;\r
- }\r
- }\r
-\r
- private Map<String, Class<SilkFunctionPlugin>> getPluginTable() {\r
- if (pluginTable == null) {\r
- pluginTable = new TreeMap<String, Class<SilkFunctionPlugin>>();\r
- // load plugins \r
- for (Class<SilkFunctionPlugin> each : FileResource.findClasses(SilkFunctionPlugin.class\r
- .getPackage(), SilkFunctionPlugin.class, SilkWalker.class.getClassLoader())) {\r
- String functionName = each.getSimpleName().toLowerCase();\r
- _logger.trace("loaded " + functionName);\r
- pluginTable.put(functionName, each);\r
- }\r
- }\r
-\r
- return pluginTable;\r
- }\r
-\r
- /**\r
- * Information of the function (plugin) arguments (\r
- * {@link SilkFunctionArgument}) described in the Class definition, which\r
- * implements {@link SilkFunctionPlugin}.\r
- * \r
- * @author leo\r
- * \r
- */\r
- private static class PluginField {\r
- Field field;\r
- SilkFunctionArgument argInfo;\r
-\r
- private PluginField(SilkFunctionArgument argInfo, Field field) {\r
- this.argInfo = argInfo;\r
- this.field = field;\r
- }\r
- }\r
-\r
- private static class PluginHolder {\r
- Class< ? extends SilkFunctionPlugin> pluginClass;\r
- ArrayList<PluginField> argumentFieldList = new ArrayList<PluginField>();\r
- Map<String, PluginField> keyValueFieldTable = new HashMap<String, PluginField>();\r
-\r
- public PluginHolder(Class< ? extends SilkFunctionPlugin> pluginClass) {\r
- this.pluginClass = pluginClass;\r
-\r
- //ArrayList<SilkFunctionArgument> argDefs = new ArrayList<SilkFunctionArgument>();\r
- for (Field eachField : pluginClass.getDeclaredFields()) {\r
- SilkFunctionArgument argInfo = eachField.getAnnotation(SilkFunctionArgument.class);\r
- if (argInfo != null) {\r
- PluginField pf = new PluginField(argInfo, eachField);\r
- if (argInfo.name().equals(SilkFunctionArgument.NO_VALUE))\r
- argumentFieldList.add(pf);\r
- else\r
- keyValueFieldTable.put(argInfo.name(), pf);\r
- }\r
- }\r
-\r
- // sort arguments in the order of their ordinal\r
- Collections.sort(argumentFieldList, new Comparator<PluginField>() {\r
- public int compare(PluginField o1, PluginField o2) {\r
- return o1.argInfo.ordinal() - o2.argInfo.ordinal();\r
- }\r
- });\r
-\r
- }\r
-\r
- /**\r
- * Bind function arguments to the plug-in instance\r
- * \r
- * @param plugin\r
- * the instance of the plug-in\r
- * @param args\r
- * the function arguments\r
- */\r
- public void populate(SilkFunctionPlugin plugin, List<SilkFunctionArg> args) {\r
- int noNameArgCount = 0;\r
- for (SilkFunctionArg eachArgument : args) {\r
- String argValue = eachArgument.getValue().toString();\r
- try {\r
- if (eachArgument.hasName()) {\r
- // key value arg\r
- PluginField f = keyValueFieldTable.get(eachArgument.getName());\r
- if (f == null) {\r
- _logger.warn("unknown argument: " + eachArgument);\r
- continue;\r
- }\r
- ReflectionUtil.setFieldValue(plugin, f.field, argValue);\r
- }\r
- else {\r
- // unnamed argument\r
- // matching argument order\r
- if (noNameArgCount >= argumentFieldList.size()) {\r
- _logger.warn(String.format(\r
- "no corresponding field for the argument %s is found",\r
- eachArgument));\r
- continue;\r
- }\r
- PluginField f = argumentFieldList.get(noNameArgCount);\r
- ReflectionUtil.setFieldValue(plugin, f.field, argValue);\r
-\r
- if (!TypeInfo.isCollection(f.field.getType()))\r
- noNameArgCount++;\r
- }\r
- }\r
- catch (XerialException e) {\r
- _logger.error(e);\r
- }\r
-\r
- }\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Fill the plug-in argument fields with the given arguments\r
- * \r
- * @param plugin\r
- * plug-in instance.\r
- * @param args\r
- * function arguments.\r
- */\r
- private static void populate(SilkFunctionPlugin plugin, List<SilkFunctionArg> args) {\r
- PluginHolder holder = new PluginHolder(plugin.getClass());\r
- holder.populate(plugin, args);\r
- }\r
-\r
- public long getNumReadLine() {\r
- return numReadLine;\r
- }\r
-}\r