1 /*--------------------------------------------------------------------------
\r
2 * Copyright 2009 Taro L. Saito
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
15 *--------------------------------------------------------------------------*/
\r
16 //--------------------------------------
\r
19 // SilkStreamReader.java
\r
20 // Since: 2009/03/31 19:53:33
\r
24 //--------------------------------------
\r
25 package org.xerial.silk;
\r
27 import java.io.BufferedReader;
\r
28 import java.io.IOException;
\r
29 import java.io.InputStream;
\r
30 import java.io.InputStreamReader;
\r
31 import java.io.Reader;
\r
32 import java.lang.reflect.Field;
\r
33 import java.net.URL;
\r
34 import java.util.ArrayList;
\r
35 import java.util.Collections;
\r
36 import java.util.Comparator;
\r
37 import java.util.HashMap;
\r
38 import java.util.List;
\r
39 import java.util.Map;
\r
40 import java.util.TreeMap;
\r
42 import org.xerial.core.XerialError;
\r
43 import org.xerial.core.XerialErrorCode;
\r
44 import org.xerial.core.XerialException;
\r
45 import org.xerial.json.JSONArray;
\r
46 import org.xerial.json.JSONException;
\r
47 import org.xerial.json.JSONObject;
\r
48 import org.xerial.json.JSONUtil;
\r
49 import org.xerial.json.JSONValue;
\r
50 import org.xerial.json.JSONValueType;
\r
51 import org.xerial.silk.impl.SilkDataLine;
\r
52 import org.xerial.silk.impl.SilkFunction;
\r
53 import org.xerial.silk.impl.SilkFunctionArg;
\r
54 import org.xerial.silk.impl.SilkJSONValue;
\r
55 import org.xerial.silk.impl.SilkNode;
\r
56 import org.xerial.silk.impl.SilkNodeOccurrence;
\r
57 import org.xerial.silk.impl.SilkValue;
\r
58 import org.xerial.silk.plugin.SilkFunctionArgument;
\r
59 import org.xerial.silk.plugin.SilkFunctionPlugin;
\r
60 import org.xerial.util.ArrayDeque;
\r
61 import org.xerial.util.FileResource;
\r
62 import org.xerial.util.bean.TypeInformation;
\r
63 import org.xerial.util.log.Logger;
\r
64 import org.xerial.util.reflect.ReflectionUtil;
\r
65 import org.xerial.util.tree.TreeEvent;
\r
66 import org.xerial.util.tree.TreeStreamReader;
\r
67 import org.xerial.util.xml.impl.TreeEventQueue;
\r
70 * {@link TreeStreamReader} implementation for the Silk data format.
\r
75 public class SilkStreamReader implements TreeStreamReader
\r
77 private static Logger _logger = Logger.getLogger(SilkStreamReader.class);
\r
79 private final SilkPullParser parser;
\r
80 private final SilkEnv parseContext;
\r
81 private TreeEventQueue eventQueue = new TreeEventQueue();
\r
82 private final ArrayDeque<TreeStreamReader> readerStack = new ArrayDeque<TreeStreamReader>();
\r
84 private int numReadLine = 0;
\r
87 * Creates a new reader with the specified input stream
\r
90 * `@throws IOException
\r
92 protected SilkStreamReader(InputStream input) throws IOException
\r
94 this(new InputStreamReader(input));
\r
98 * Creates a new reader with the specified reader
\r
101 * @throws IOException
\r
103 protected SilkStreamReader(Reader input) throws IOException
\r
105 this(input, SilkEnv.newEnv());
\r
109 * Creates a new reader inherited the given environment
\r
113 * @throws IOException
\r
115 public SilkStreamReader(Reader input, SilkEnv env) throws IOException
\r
117 this.parser = new SilkPullParser(input);
\r
118 this.parseContext = env;
\r
122 * Concatenates the base path and the resource name
\r
124 * @param resourceBasePath
\r
125 * @param resourceName
\r
128 private static String getResourcePath(String resourceBasePath, String resourceName)
\r
130 String resourcePath = resourceBasePath;
\r
131 if (!resourcePath.endsWith("/"))
\r
132 resourcePath += "/";
\r
133 resourcePath += resourceName;
\r
134 return resourcePath;
\r
138 * Create a new reader for reading local resources
\r
140 * @param resourceBasePath
\r
141 * @param resourceName
\r
142 * @throws IOException
\r
144 public SilkStreamReader(String resourceBasePath, String resourceName) throws IOException
\r
146 this.parser = new SilkPullParser(new BufferedReader(new InputStreamReader(SilkWalker.class
\r
147 .getResourceAsStream(getResourcePath(resourceBasePath, resourceName)))));
\r
148 this.parseContext = SilkEnv.newEnv(resourceBasePath);
\r
152 * Create a new reader for reading the specified resource URL
\r
154 * @param resourcePath
\r
155 * @throws IOException
\r
157 public SilkStreamReader(URL resourcePath) throws IOException
\r
159 this(resourcePath, SilkEnv.newEnv());
\r
162 public SilkStreamReader(URL resource, SilkEnv env) throws IOException
\r
164 String path = resource.toExternalForm();
\r
165 int fileNamePos = path.lastIndexOf("/");
\r
166 String resourceBasePath = fileNamePos > 0 ? path.substring(0, fileNamePos) : null;
\r
168 this.parser = new SilkPullParser(new BufferedReader(new InputStreamReader(resource.openStream())));
\r
169 this.parseContext = SilkEnv.newEnv(env, resourceBasePath);
\r
172 public TreeEvent peekNext() throws XerialException
\r
175 return eventQueue.peekFirst();
\r
180 public TreeEvent next() throws XerialException
\r
183 return getNextEvent();
\r
189 * Enqueues a visit event.
\r
192 * @param immediateNodeValue
\r
193 * @throws XerialException
\r
195 private void visit(String nodeName, String immediateNodeValue) throws XerialException
\r
197 eventQueue.push(TreeEvent.newVisitEvent(nodeName, immediateNodeValue));
\r
201 * Enqueues a leave event
\r
204 * @throws XerialException
\r
206 private void leave(String nodeName) throws XerialException
\r
208 eventQueue.push(TreeEvent.newLeaveEvent(nodeName));
\r
212 * Enqueues a text event
\r
214 * @param textFragment
\r
215 * @throws XerialException
\r
217 private void text(String textFragment) throws XerialException
\r
219 eventQueue.push(TreeEvent.newTextEvent(parseContext.getContextNode().getName(), textFragment));
\r
223 * Closed pre-opened contexts up to the specified indent level
\r
225 * @param newIndentLevel
\r
226 * @throws XerialException
\r
228 private void closeContextUpTo(int newIndentLevel) throws XerialException
\r
230 while (!parseContext.isContextNodeStackEmpty())
\r
232 SilkContext context = parseContext.peekLastContext();
\r
233 SilkNode node = context.contextNode;
\r
234 if (node.getIndentLevel() >= newIndentLevel)
\r
236 parseContext.popLastContext();
\r
238 if (parseContext.isAttributeOpen)
\r
240 // close attribute
\r
241 SilkNode attribute = node.getChildNodes().get(parseContext.contextNodeAttributeCursor);
\r
242 leave(attribute.getName());
\r
243 leave(node.getName());
\r
244 parseContext.isAttributeOpen = false;
\r
247 if (context.isOpen)
\r
250 String nodeName = node.getName();
\r
260 * Opens a new context for the given node
\r
265 * @throws XerialException
\r
267 private void openContext(SilkNode node) throws XerialException
\r
269 int indentLevel = node.getIndentLevel();
\r
270 if (indentLevel != SilkNode.NO_INDENT)
\r
271 indentLevel += parseContext.getIndentationOffset();
\r
273 closeContextUpTo(indentLevel);
\r
274 openContext_internal(node);
\r
277 private void openContext_internal(SilkNode node) throws XerialException
\r
279 if (node.getName() == null)
\r
281 // no name nodes must hierarchically organize attribute nodes
\r
282 for (SilkNode eachChild : node.getChildNodes())
\r
284 eachChild.setNodeIndent(node.getNodeIndent());
\r
285 openContext_internal(eachChild);
\r
290 SilkContext currentContext = new SilkContext(node, true);
\r
291 parseContext.pushContext(currentContext);
\r
293 SilkNodeOccurrence occurrence = node.getOccurrence();
\r
294 if (occurrence.isSchemaOnlyNode())
\r
296 currentContext.isOpen = false;
\r
297 // reset the attribute cursor
\r
298 parseContext.contextNodeAttributeCursor = 0;
\r
299 parseContext.isAttributeOpen = false;
\r
300 return; // do not invoke visit events
\r
303 String nodeName = node.getName();
\r
304 SilkValue textValue = node.getValue();
\r
306 // process text values attached to the node
\r
307 if (textValue != null)
\r
309 // When the text data is JSON, traverses the JSON data
\r
310 if (textValue.isJSON())
\r
313 SilkJSONValue jsonValue = SilkJSONValue.class.cast(textValue);
\r
314 if (jsonValue.isObject())
\r
316 visit(nodeName, null);
\r
317 JSONObject jsonObj = new JSONObject(jsonValue.getValue());
\r
318 walkJSONObject(jsonObj);
\r
322 currentContext.isOpen = false;
\r
323 JSONArray jsonArray = new JSONArray(jsonValue.getValue());
\r
324 walkJSONAray(jsonArray, nodeName);
\r
327 else if (textValue.isFunction())
\r
329 // evaluate the function
\r
330 visit(nodeName, null);
\r
331 SilkFunction function = SilkFunction.class.cast(textValue);
\r
332 evalFunction(function);
\r
338 // Simple text value will be reported as it is.
\r
339 visit(nodeName, textValue.toString());
\r
344 if (occurrence == SilkNodeOccurrence.ZERO_OR_MORE)
\r
347 return; // do not invoke visit events
\r
350 // Report a visit event without text value
\r
351 visit(nodeName, null);
\r
354 // Traverse attribute nodes having text values. If no text value is specified for these attributes,
\r
355 // they are schema elements for the following DATA_LINE.
\r
356 for (SilkNode eachChild : node.getChildNodes())
\r
358 if (eachChild.hasValue())
\r
360 openContext(eachChild);
\r
366 private static class FunctionReader implements TreeStreamReader
\r
368 SilkFunctionPlugin plugin;
\r
370 public FunctionReader(SilkFunctionPlugin plugin)
\r
372 this.plugin = plugin;
\r
375 public TreeEvent peekNext() throws XerialException
\r
377 return plugin.peekNext();
\r
380 public TreeEvent next() throws XerialException
\r
382 return plugin.next();
\r
387 * Evaluate the function
\r
390 * @throws XerialException
\r
392 private void evalFunction(SilkFunction function) throws XerialException
\r
394 SilkFunctionPlugin plugin = getPlugin(function.getName());
\r
395 if (plugin == null)
\r
397 _logger.error(String.format("plugin %s not found", function.getName()));
\r
400 // fill the function argument to the plugin instance
\r
401 populate(plugin, function.getArgumentList());
\r
403 // evaluate the function
\r
404 SilkEnv env = parseContext.newEnvFor(function);
\r
407 readerStack.addLast(new FunctionReader(plugin));
\r
411 * Has finished reading the stream?
\r
413 private boolean hasFinished = false;
\r
416 * Is next event available?
\r
418 * @return true if there are remaining events, otherwise fales
\r
419 * @throws XerialException
\r
421 private boolean hasNext() throws XerialException
\r
423 if (!eventQueue.isEmpty())
\r
429 while (!hasFinished && eventQueue.isEmpty())
\r
436 * Retrieves the next event from the queue. If the event queue is empty,
\r
437 * fill the queue with the next event
\r
439 * @return the next event.
\r
440 * @throws XerialException
\r
442 private TreeEvent getNextEvent() throws XerialException
\r
444 if (!eventQueue.isEmpty())
\r
445 return eventQueue.pop();
\r
448 throw new XerialError(XerialErrorCode.INVALID_STATE,
\r
449 "hasNext() value must be checked before calling getNextEvent()");
\r
452 return getNextEvent();
\r
455 private void walkMicroFormatRoot(SilkNode schemaNode, JSONArray value) throws XerialException
\r
457 // e.g., exon(start, name)
\r
459 if (schemaNode.hasManyOccurrences())
\r
461 if (schemaNode.hasChildren())
\r
463 // e.g., exon(start, name)*
\r
464 // multiple occurrences: [[start, end], [start, end], ... ]
\r
465 for (int i = 0; i < value.size(); i++)
\r
467 JSONArray eachElement = value.getJSONArray(i);
\r
468 if (eachElement == null)
\r
471 visit(schemaNode.getName(), null);
\r
473 for (SilkNode eachSubSchema : schemaNode.getChildNodes())
\r
475 walkMicroFormatElement(eachSubSchema, eachElement.get(index++));
\r
477 leave(schemaNode.getName());
\r
482 // e.g. QV*: [20, 50, 50]
\r
483 for (int i = 0; i < value.size(); i++)
\r
485 visit(schemaNode.getName(), value.get(i).toString());
\r
486 leave(schemaNode.getName());
\r
493 visit(schemaNode.getName(), null);
\r
495 if (schemaNode.getChildNodes().size() != value.size())
\r
497 throw new XerialException(XerialErrorCode.INVALID_INPUT, String.format(
\r
498 "data format doesn't match: schema=%s, value=%s", schemaNode, value));
\r
500 for (SilkNode each : schemaNode.getChildNodes())
\r
502 walkMicroFormatElement(each, value.get(index++));
\r
504 leave(schemaNode.getName());
\r
508 private void walkMicroFormatElement(SilkNode schemaNode, JSONValue value) throws XerialException
\r
510 if (schemaNode.hasChildren())
\r
512 JSONArray array = value.getJSONArray();
\r
514 walkMicroFormatRoot(schemaNode, array);
\r
516 throw new XerialException(XerialErrorCode.INVALID_INPUT, String.format(
\r
517 "data format doesn't match: schema=%s, value=%s", schemaNode, value));
\r
521 visit(schemaNode.getName(), value.toString());
\r
522 leave(schemaNode.getName());
\r
526 private void evalDatalineColumn(SilkNode node, String columnData) throws XerialException
\r
530 if (node.hasChildren())
\r
532 JSONArray array = new JSONArray(columnData);
\r
533 walkMicroFormatRoot(node, array);
\r
537 switch (node.getOccurrence())
\r
541 if (columnData.startsWith("["))
\r
543 // micro-data format
\r
547 JSONArray array = new JSONArray(columnData);
\r
548 // 1400 lines/sec (ANTLR) 4200 lines/sec (JSONTokener)
\r
550 // 5233 lines/sec w/o traversing JSONArray
\r
554 // visit(node.getName(), null);
\r
555 // leave(node.getName());
\r
558 walkMicroFormatRoot(node, array);
\r
563 String[] csv = columnData.split(",");
\r
564 for (String each : csv)
\r
566 String value = each.trim();
\r
567 evalColumnData(node, value);
\r
572 evalColumnData(node, columnData);
\r
578 private void evalColumnData(SilkNode node, String columnData) throws XerialException
\r
582 if (node.hasChildren())
\r
584 // micro-data format
\r
585 JSONArray array = new JSONArray(columnData);
\r
586 walkMicroFormatRoot(node, array);
\r
590 String dataType = node.getDataType();
\r
591 if (dataType != null && dataType.equalsIgnoreCase("json"))
\r
593 JSONValue json = JSONUtil.parseJSON(columnData);
\r
594 if (json.getJSONObject() != null)
\r
596 if (node.getName().equals("_")) // no name object
\r
598 walkJSONValue(json, node.getName());
\r
602 visit(node.getName(), null);
\r
603 walkJSONValue(json, node.getName());
\r
604 leave(node.getName());
\r
608 walkJSONValue(json, node.getName());
\r
612 visit(node.getName(), columnData);
\r
613 leave(node.getName());
\r
616 catch (JSONException e)
\r
618 throw new XerialException(e.getErrorCode(), String.format("line=%d: %s", parser.getNumReadLine(), e
\r
625 * Fill the queue by retrieving the next event from the pull parser.
\r
627 * @throws XerialException
\r
629 private void fillQueue() throws XerialException
\r
631 if (!readerStack.isEmpty())
\r
633 TreeEvent e = readerStack.peekLast().next();
\r
636 readerStack.removeLast();
\r
641 eventQueue.push(e);
\r
646 if (!parser.hasNext())
\r
648 // no more input data
\r
649 closeContextUpTo(parseContext.getIndentationOffset());
\r
650 hasFinished = true;
\r
654 SilkEvent currentEvent = parser.next();
\r
656 // update the line count
\r
657 numReadLine = parser.getNumReadLine();
\r
659 if (_logger.isTraceEnabled())
\r
661 _logger.trace("stack: " + parseContext.getContextNodeStack());
\r
662 _logger.trace(currentEvent);
\r
665 switch (currentEvent.getType())
\r
668 // push context node
\r
669 SilkNode newContextNode = SilkNode.class.cast(currentEvent.getElement());
\r
670 openContext(newContextNode);
\r
673 SilkFunction function = SilkFunction.class.cast(currentEvent.getElement());
\r
674 evalFunction(function);
\r
677 // pop the context stack until finding a node for stream data node occurrence
\r
678 while (!parseContext.isContextNodeStackEmpty())
\r
680 SilkContext context = parseContext.peekLastContext();
\r
681 SilkNode node = context.contextNode;
\r
682 if (!node.getOccurrence().isFollowedByStreamData())
\r
684 parseContext.popLastContext();
\r
685 if (context.isOpen)
\r
686 leave(node.getName());
\r
692 if (parseContext.isContextNodeStackEmpty())
\r
694 // use default column names(c1, c2, ...)
\r
695 SilkDataLine line = SilkDataLine.class.cast(currentEvent.getElement());
\r
696 String[] columns = line.getDataLine().trim().split("\t");
\r
698 visit("row", null);
\r
699 for (String each : columns)
\r
701 String columnName = String.format("c%d", index++);
\r
703 // TODO use evalColumnData
\r
704 visit(columnName, each);
\r
711 SilkContext context = parseContext.peekLastContext();
\r
712 SilkNode schema = context.contextNode;
\r
713 SilkDataLine line = SilkDataLine.class.cast(currentEvent.getElement());
\r
714 switch (schema.getOccurrence())
\r
717 text(line.getDataLine());
\r
722 evalDatalineColumn(schema, line.getDataLine());
\r
725 case TABBED_SEQUENCE:
\r
727 String[] columns = line.getDataLine().trim().split("\t");
\r
728 int columnIndex = 0;
\r
729 visit(schema.getName(), schema.hasValue() ? schema.getValue().toString() : null);
\r
730 for (int i = 0; i < schema.getChildNodes().size(); i++)
\r
732 SilkNode child = schema.getChildNodes().get(i);
\r
733 if (child.hasValue())
\r
735 // output the default value for the column
\r
736 evalDatalineColumn(child, child.getValue().toString());
\r
740 if (columnIndex < columns.length)
\r
742 String columnData = columns[columnIndex++];
\r
743 evalDatalineColumn(child, columnData);
\r
747 leave(schema.getName());
\r
750 case MULTILINE_SEQUENCE:
\r
752 int cursor = parseContext.contextNodeAttributeCursor;
\r
754 if (cursor >= schema.getChildNodes().size())
\r
757 SilkNode attributeNode = schema.getChildNodes().get(cursor);
\r
758 if (cursor == 0 && !parseContext.isAttributeOpen)
\r
760 visit(schema.getName(), schema.hasValue() ? schema.getValue().toString() : null);
\r
762 if (!parseContext.isAttributeOpen)
\r
764 if (attributeNode.hasValue())
\r
765 visit(attributeNode.getName(), attributeNode.getValue().toString());
\r
767 visit(attributeNode.getName(), null);
\r
769 parseContext.isAttributeOpen = true;
\r
771 text(line.getDataLine().trim());
\r
777 case MULTILINE_ENTRY_SEPARATOR: // >>
\r
779 SilkContext context = parseContext.peekLastContext();
\r
780 SilkNode schema = context.contextNode;
\r
781 if (parseContext.isAttributeOpen)
\r
783 SilkNode attributeNode = schema.getChildNodes().get(parseContext.contextNodeAttributeCursor);
\r
784 leave(attributeNode.getName());
\r
786 leave(schema.getName());
\r
788 parseContext.contextNodeAttributeCursor = 0;
\r
789 parseContext.isAttributeOpen = false;
\r
792 case MULTILINE_SEPARATOR: // --
\r
794 SilkContext context = parseContext.peekLastContext();
\r
795 SilkNode schema = context.contextNode;
\r
796 if (parseContext.isAttributeOpen)
\r
798 SilkNode attributeNode = schema.getChildNodes().get(parseContext.contextNodeAttributeCursor);
\r
799 leave(attributeNode.getName());
\r
801 parseContext.contextNodeAttributeCursor++;
\r
802 parseContext.isAttributeOpen = false;
\r
815 private void walkJSONAray(JSONArray jsonArray, String parentNodeName) throws XerialException
\r
817 for (JSONValue each : jsonArray)
\r
819 walkJSONValue(each, parentNodeName);
\r
823 private void walkJSONObject(JSONObject jsonObj) throws XerialException
\r
825 for (String key : jsonObj.keys())
\r
827 JSONValue val = jsonObj.get(key);
\r
828 walkJSONValue(val, key);
\r
832 private void walkJSONValue(JSONValue value, String parentNodeName) throws XerialException
\r
834 JSONValueType type = value.getValueType();
\r
838 walkJSONAray(value.getJSONArray(), parentNodeName);
\r
841 walkJSONObject(value.getJSONObject());
\r
844 visit(parentNodeName, value.toString());
\r
845 leave(parentNodeName);
\r
848 visit(parentNodeName, value.toString());
\r
849 leave(parentNodeName);
\r
852 visit(parentNodeName, value.toString());
\r
853 leave(parentNodeName);
\r
856 visit(parentNodeName, value.toString());
\r
857 leave(parentNodeName);
\r
860 visit(parentNodeName, value.toString());
\r
861 leave(parentNodeName);
\r
870 private static Map<String, Class<SilkFunctionPlugin>> pluginTable = null;
\r
873 * Get the plugin of the specified name
\r
877 * @return plugin instance or null if no corresponding plugin is found.
\r
879 private SilkFunctionPlugin getPlugin(String name)
\r
881 Class<SilkFunctionPlugin> pluginClass = getPluginTable().get(name);
\r
882 if (pluginClass == null)
\r
887 SilkFunctionPlugin pluginInstance = pluginClass.newInstance();
\r
888 return pluginInstance;
\r
890 catch (InstantiationException e)
\r
895 catch (IllegalAccessException e)
\r
902 private Map<String, Class<SilkFunctionPlugin>> getPluginTable()
\r
904 if (pluginTable == null)
\r
906 pluginTable = new TreeMap<String, Class<SilkFunctionPlugin>>();
\r
908 for (Class<SilkFunctionPlugin> each : FileResource.findClasses(SilkFunctionPlugin.class.getPackage(),
\r
909 SilkFunctionPlugin.class, SilkWalker.class.getClassLoader()))
\r
911 String functionName = each.getSimpleName().toLowerCase();
\r
912 _logger.trace("loaded " + functionName);
\r
913 pluginTable.put(functionName, each);
\r
917 return pluginTable;
\r
921 * Information of the function (plugin) arguments (
\r
922 * {@link SilkFunctionArgument}) described in the Class definition, which
\r
923 * implements {@link SilkFunctionPlugin}.
\r
928 private static class PluginField
\r
931 SilkFunctionArgument argInfo;
\r
933 private PluginField(SilkFunctionArgument argInfo, Field field)
\r
935 this.argInfo = argInfo;
\r
936 this.field = field;
\r
940 private static class PluginHolder
\r
942 Class< ? extends SilkFunctionPlugin> pluginClass;
\r
943 ArrayList<PluginField> argumentFieldList = new ArrayList<PluginField>();
\r
944 Map<String, PluginField> keyValueFieldTable = new HashMap<String, PluginField>();
\r
946 public PluginHolder(Class< ? extends SilkFunctionPlugin> pluginClass)
\r
948 this.pluginClass = pluginClass;
\r
950 //ArrayList<SilkFunctionArgument> argDefs = new ArrayList<SilkFunctionArgument>();
\r
951 for (Field eachField : pluginClass.getDeclaredFields())
\r
953 SilkFunctionArgument argInfo = eachField.getAnnotation(SilkFunctionArgument.class);
\r
954 if (argInfo != null)
\r
956 PluginField pf = new PluginField(argInfo, eachField);
\r
957 if (argInfo.name().equals(SilkFunctionArgument.NO_VALUE))
\r
958 argumentFieldList.add(pf);
\r
960 keyValueFieldTable.put(argInfo.name(), pf);
\r
964 // sort arguments in the order of their ordinal
\r
965 Collections.sort(argumentFieldList, new Comparator<PluginField>() {
\r
966 public int compare(PluginField o1, PluginField o2)
\r
968 return o1.argInfo.ordinal() - o2.argInfo.ordinal();
\r
975 * Bind function arguments to the plug-in instance
\r
978 * the instance of the plug-in
\r
980 * the function arguments
\r
982 public void populate(SilkFunctionPlugin plugin, List<SilkFunctionArg> args)
\r
984 int noNameArgCount = 0;
\r
985 for (SilkFunctionArg eachArgument : args)
\r
987 String argValue = eachArgument.getValue().toString();
\r
990 if (eachArgument.hasName())
\r
993 PluginField f = keyValueFieldTable.get(eachArgument.getName());
\r
996 _logger.warn("unknown argument: " + eachArgument);
\r
999 ReflectionUtil.setFieldValue(plugin, f.field, argValue);
\r
1003 // unnamed argument
\r
1004 // matching argument order
\r
1005 if (noNameArgCount >= argumentFieldList.size())
\r
1007 _logger.warn(String.format("no corresponding field for the argument %s is found",
\r
1011 PluginField f = argumentFieldList.get(noNameArgCount);
\r
1012 ReflectionUtil.setFieldValue(plugin, f.field, argValue);
\r
1014 if (!TypeInformation.isCollection(f.field.getType()))
\r
1018 catch (XerialException e)
\r
1029 * Fill the plug-in argument fields with the given arguments
\r
1032 * plug-in instance.
\r
1034 * function arguments.
\r
1036 private static void populate(SilkFunctionPlugin plugin, List<SilkFunctionArg> args)
\r
1038 PluginHolder holder = new PluginHolder(plugin.getClass());
\r
1039 holder.populate(plugin, args);
\r
1042 public int getNumReadLine()
\r
1044 return numReadLine;
\r