package org.xerial.json;
import java.io.IOException;
+import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.LinkedList;
*/
public class JSONWriter
{
- private final Writer writer;
+ private final PrintWriter writer;
enum JSONState {
InObject, InArray, InString, Root, Unknown
public JSONWriter(Writer writer)
{
- this.writer = writer;
+ this.writer = new PrintWriter(writer);
stateStack.add(JSONState.Root);
elementCountStack.add(0);
}
elementCountStack.add(++count);
}
- public void startObject() throws IOException
+ public void startObject()
{
if (getCurrentState() == JSONState.InArray)
putComma();
- writer.append("{");
+ writer.print("{");
pushState(JSONState.InObject);
}
- public void endObject() throws IOException
+ public void endObject()
{
if (getCurrentState() != JSONState.InObject)
throw new XerialError(JSONErrorCode.NotInAJSONObject, "cannot end the object outside of the JSON object");
- writer.append("}");
+ writer.print("}");
popState();
if (getCurrentState() == JSONState.InArray)
incrementElementCount();
}
- public void startArray() throws IOException
+ public void startArray()
{
- writer.append("[");
+ writer.print("[");
pushState(JSONState.InArray);
}
- public void endArray() throws IOException
+ public void endArray()
{
if (getCurrentState() != JSONState.InArray)
throw new XerialError(JSONErrorCode.NotInAJSONArray, "cannot end the arry outside of the JSON array");
- writer.append("]");
+ writer.print("]");
popState();
}
- public void startString() throws IOException
+ public void startString()
{
if (getCurrentState() == JSONState.InArray)
throw new XerialError(JSONErrorCode.NotInAJSONArray,
pushState(JSONState.InString);
}
- public void startString(String key) throws IOException
+ public void startString(String key)
{
outputKeyPart(key);
writer.append("\"");
pushState(JSONState.InString);
}
- public void append(String stringFragment) throws IOException
+ public void append(String stringFragment)
{
if (getCurrentState() != JSONState.InString)
throw new XerialError(JSONErrorCode.NotInAJSONString,
popState();
}
- public void startArray(String key) throws IOException
+ public void startArray(String key)
{
if (getCurrentState() != JSONState.InObject)
throw new XerialError(JSONErrorCode.NotInAJSONObject,
"cannot start a keyed array outside of the JSON object");
if (getPreviousElementCount() > 0)
- writer.append(",");
- writer.append(doubleQuote(key));
- writer.append(":[");
+ writer.print(",");
+ writer.print(doubleQuote(key));
+ writer.print(":[");
incrementElementCount();
pushState(JSONState.InArray);
}
incrementElementCount();
}
- private void putComma() throws IOException
+ private void putComma()
{
if (getPreviousElementCount() > 0)
- writer.append(",");
+ writer.print(",");
}
public void put(String key, String value) throws IOException
* @param key
* @param input
* @throws IOException
+ * @throws IOException
*/
public void putString(String key, Reader input) throws IOException
{
incrementElementCount();
}
- private void outputKeyPart(String key) throws IOException
+ private void outputKeyPart(String key)
{
if (getCurrentState() != JSONState.InObject)
throw new XerialError(JSONErrorCode.NotInAJSONObject,
writer.append(":");
}
- public void flush() throws IOException
+ public void flush()
{
writer.flush();
}
- public void endJSON() throws IOException
+ public void endJSON()
{
for (ListIterator<JSONState> it = stateStack.listIterator(stateStack.size()); it.hasPrevious();)
{
--- /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
+// SilkUtil.java\r
+// Since: 2009/02/13 21:15:32\r
+//\r
+// $URL$\r
+// $Author$\r
+//--------------------------------------\r
+package org.xerial.silk;\r
+\r
+import java.io.IOException;\r
+import java.io.StringWriter;\r
+import java.io.Writer;\r
+import java.net.URL;\r
+import java.util.ArrayDeque;\r
+import java.util.Deque;\r
+\r
+import org.xerial.core.XerialError;\r
+import org.xerial.core.XerialException;\r
+import org.xerial.json.JSONWriter;\r
+import org.xerial.util.tree.TreeEvent;\r
+import org.xerial.util.tree.TreeVisitor;\r
+import org.xerial.util.tree.TreeVisitorBase;\r
+import org.xerial.util.tree.TreeWalker;\r
+import org.xerial.util.xml.XMLAttribute;\r
+import org.xerial.util.xml.XMLErrorCode;\r
+import org.xerial.util.xml.XMLGenerator;\r
+\r
+/**\r
+ * Utilities for handling Silk file format.\r
+ * \r
+ * @author leo\r
+ * \r
+ */\r
+public class SilkUtil\r
+{\r
+ public static String toXML(URL silkSource) throws IOException, XerialException\r
+ {\r
+ StringWriter buf = new StringWriter();\r
+ toXML(silkSource, buf);\r
+ return buf.toString();\r
+ }\r
+\r
+ /**\r
+ * Convert the silk file into XML\r
+ * \r
+ * @param silkSource\r
+ * @param out\r
+ * @throws IOException\r
+ * @throws XerialException\r
+ */\r
+ public static void toXML(URL silkSource, Writer out) throws IOException, XerialException\r
+ {\r
+ SilkWalker walker = new SilkWalker(silkSource);\r
+ XMLBuilder builder = new XMLBuilder(out);\r
+ walker.walk(builder);\r
+\r
+ }\r
+\r
+ /**\r
+ * \r
+ * <pre>\r
+ * 1:visit -> 2:visit : pop(1), startTag(1, value=".."), push(2:visit)\r
+ * 1:visit -> 2:text : pop(1), startTag(1, value=".."), text(2:text) \r
+ * 1:visit -> 2:leave : pop(1), selfCloseTag(1, value="..")\r
+ * </pre>\r
+ * \r
+ * @author leo\r
+ * \r
+ */\r
+ private static class XMLBuilder extends TreeVisitorBase\r
+ {\r
+ final XMLGenerator xout;\r
+ final Deque<TreeEvent> eventQueue = new ArrayDeque<TreeEvent>();\r
+\r
+ public XMLBuilder(Writer out)\r
+ {\r
+ xout = new XMLGenerator(out);\r
+ }\r
+\r
+ private void popQueue()\r
+ {\r
+ if (!eventQueue.isEmpty())\r
+ {\r
+ TreeEvent prev = eventQueue.removeLast();\r
+\r
+ switch (prev.event)\r
+ {\r
+ case VISIT:\r
+ if (prev.nodeValue == null)\r
+ xout.startTag(prev.nodeName);\r
+ else\r
+ xout.startTag(prev.nodeName, new XMLAttribute("value", prev.nodeValue));\r
+ break;\r
+ default:\r
+ throw new XerialError(XMLErrorCode.INVALID_XML_STRUCTURE);\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ @Override\r
+ public void visitNode(String nodeName, String immediateNodeValue, TreeWalker walker) throws XerialException\r
+ {\r
+ popQueue();\r
+ eventQueue.addLast(new TreeEvent(TreeEvent.EventType.VISIT, nodeName, immediateNodeValue));\r
+ }\r
+\r
+ @Override\r
+ public void text(String textDataFragment, TreeWalker walker) throws XerialException\r
+ {\r
+ popQueue();\r
+ xout.text(textDataFragment);\r
+ }\r
+\r
+ @Override\r
+ public void leaveNode(String nodeName, TreeWalker walker) throws XerialException\r
+ {\r
+ if (!eventQueue.isEmpty())\r
+ {\r
+ TreeEvent prev = eventQueue.removeLast();\r
+\r
+ switch (prev.event)\r
+ {\r
+ case VISIT:\r
+ if (prev.nodeValue == null)\r
+ xout.selfCloseTag(prev.nodeName);\r
+ else\r
+ xout.element(prev.nodeName, prev.nodeValue);\r
+ }\r
+ }\r
+ else\r
+ xout.endTag();\r
+ }\r
+\r
+ @Override\r
+ public void init(TreeWalker walker) throws XerialException\r
+ {\r
+ visitNode("silk", null, walker);\r
+ }\r
+\r
+ @Override\r
+ public void finish(TreeWalker walker) throws XerialException\r
+ {\r
+ leaveNode("silk", walker);\r
+ xout.endDocument();\r
+ }\r
+ }\r
+\r
+ public static void toJSON(URL silkSource, Writer out) throws IOException, XerialException\r
+ {\r
+ SilkWalker walker = new SilkWalker(silkSource);\r
+ JSONBuilder builder = new JSONBuilder(out);\r
+ walker.walk(builder);\r
+ }\r
+\r
+ private static class JSONBuilder implements TreeVisitor\r
+ {\r
+ final JSONWriter jout;\r
+\r
+ public JSONBuilder(Writer out)\r
+ {\r
+ jout = new JSONWriter(out);\r
+ }\r
+\r
+ public void finish(TreeWalker walker) throws XerialException\r
+ {\r
+ jout.endArray();\r
+\r
+ }\r
+\r
+ public void init(TreeWalker walker) throws XerialException\r
+ {\r
+ jout.startArray();\r
+\r
+ }\r
+\r
+ public void leaveNode(String nodeName, TreeWalker walker) throws XerialException\r
+ {\r
+ // TODO Auto-generated method stub\r
+\r
+ }\r
+\r
+ public void text(String textDataFragment, TreeWalker walker) throws XerialException\r
+ {\r
+ // TODO Auto-generated method stub\r
+\r
+ }\r
+\r
+ public void visitNode(String nodeName, String immediateNodeValue, TreeWalker walker) throws XerialException\r
+ {\r
+ // TODO Auto-generated method stub\r
+\r
+ }\r
+\r
+ }\r
+\r
+ /**\r
+ * Forbid construction\r
+ */\r
+ protected SilkUtil()\r
+ {}\r
+\r
+}\r
import java.util.Map;
import java.util.TreeMap;
+import org.xerial.core.XerialError;
+import org.xerial.core.XerialErrorCode;
import org.xerial.core.XerialException;
import org.xerial.json.JSONArray;
import org.xerial.json.JSONException;
/**
* {@link TreeWalker} implementation of the Silk format.
*
- * <pre>
- * @
- * </pre>
- *
* @author leo
*
*/
* @param input
* `@throws IOException
*/
- public SilkWalker(InputStream input) throws IOException
+ protected SilkWalker(InputStream input) throws IOException
{
this.parser = new SilkPullParser(input);
init();
* @param input
* @throws IOException
*/
- public SilkWalker(Reader input) throws IOException
+ protected SilkWalker(Reader input) throws IOException
{
this.parser = new SilkPullParser(input);
init();
init();
}
- public SilkWalker(URL resource) throws IOException
+ public SilkWalker(URL resourcePath) throws IOException
{
- String path = resource.toExternalForm();
+ String path = resourcePath.toExternalForm();
int fileNamePos = path.lastIndexOf("/");
this.resourceBasePath = fileNamePos > 0 ? path.substring(0, fileNamePos) : null;
- this.parser = new SilkPullParser(new BufferedReader(new InputStreamReader(resource.openStream())));
+ this.parser = new SilkPullParser(new BufferedReader(new InputStreamReader(resourcePath.openStream())));
init();
}
{
while (!builder.hasFinished())
{
- step();
+ stepNext();
}
}
finally
return builder.getSubtreeRoot();
}
+ /**
+ * If this stack is not empty, we must disable event reporting to the
+ * {@link TreeVisitor}
+ */
private ArrayDeque<String> skipNodeStack = new ArrayDeque<String>();
public void skipDescendants()
skipNodeStack.addLast(currentEvent.nodeName);
}
+ /**
+ * Enqueues a visit event.
+ *
+ * @param nodeName
+ * @param immediateNodeValue
+ * @throws XerialException
+ */
private void visit(String nodeName, String immediateNodeValue) throws XerialException
{
eventQueue.addLast(new TreeEvent(TreeEvent.EventType.VISIT, nodeName, immediateNodeValue));
}
+ /**
+ * Enqueues a leave event
+ *
+ * @param nodeName
+ * @throws XerialException
+ */
private void leave(String nodeName) throws XerialException
{
eventQueue.addLast(new TreeEvent(TreeEvent.EventType.LEAVE, nodeName, null));
}
+ /**
+ * Enqueues a text event
+ *
+ * @param textFragment
+ * @throws XerialException
+ */
private void text(String textFragment) throws XerialException
{
eventQueue.addLast(new TreeEvent(TreeEvent.EventType.TEXT, null, textFragment));
}
- private void closeUpTo(int newIndentLevel) throws XerialException
+ /**
+ * Closed pre-opened contexts up to the specified indent level
+ *
+ * @param newIndentLevel
+ * @throws XerialException
+ */
+ private void closeContextUpTo(int newIndentLevel) throws XerialException
{
// if (newIndentLevel == SilkNode.NO_INDENT)
// newIndentLevel = contextNodeStack.isEmpty() ? 0 : contextNodeStack.peekLast().getIndentLevel() + 1;
//outputDataCountStack.removeLast();
if (node.getOccurrence() != SilkNodeOccurrence.TABBED_SEQUENCE)
- closeContext(node);
+ {
+ // close context
+ String nodeName = node.getName();
+ leave(nodeName);
+ }
}
else
return;
}
}
- private void closeContext(SilkNode node) throws XerialException
- {
- String nodeName = node.getName();
- leave(nodeName);
- }
-
- private void openContext(SilkNode node, TreeVisitor visitor) throws XerialException
+ /**
+ * Opens a new context for the given node
+ *
+ * @param node
+ * new context node
+ * @param visitor
+ * @throws XerialException
+ */
+ private void openContext(SilkNode node) throws XerialException
{
int indentLevel = node.getIndentLevel();
if (indentLevel != SilkNode.NO_INDENT)
indentLevel += indentationOffset;
- closeUpTo(indentLevel);
+ closeContextUpTo(indentLevel);
contextNodeStack.addLast(node);
//outputDataCountStack.addLast(0);
SilkValue textValue = node.getValue();
+ // process text values attached to the node
if (textValue != null)
{
+ // When the text data is JSON, traverses the JSON data
if (textValue.isJSON())
{
visit(nodeName, null);
}
else if (textValue.isFunction())
{
+ // evaluate the function
visit(nodeName, null);
SilkFunction function = SilkFunction.class.cast(textValue);
- evalFunction(function, visitor);
+ evalFunction(function);
return;
}
else
+ {
+ // Simple text value will be reported as it is.
visit(nodeName, textValue.toString());
+ }
}
else
+ {
+ // Report a visit event without text value
visit(nodeName, null);
+ }
- // traverse attribute nodes with text values
+ // Traverse attribute nodes which have text values. If no text value is specified for an attribute,
+ // this attribute is a schema element for the following DATA_LINE
for (SilkNode eachChild : node.getChildNodes())
{
if (eachChild.hasValue())
{
- openContext(eachChild, visitor);
+ openContext(eachChild);
}
}
}
- private void evalFunction(SilkFunction function, TreeVisitor visitor) throws XerialException
+ /**
+ * Evaluate the function
+ *
+ * @param function
+ * @throws XerialException
+ */
+ private void evalFunction(SilkFunction function) throws XerialException
{
if (!skipNodeStack.isEmpty())
return; // skip evaluation
_logger.error(String.format("plugin %s not found", function.getName()));
return;
}
+ // fill the function argument to the plugin instance
populate(plugin, function.getArgumentList());
plugin.eval(new SilkEnvImpl(function, visitor));
while (hasNext())
{
- step();
+ stepNext();
}
}
- private void step() throws XerialException
+ /**
+ * Consume the next event, and call its corresponding visitor event.
+ *
+ * @throws XerialException
+ */
+ private void stepNext() throws XerialException
{
- currentEvent = next();
+ currentEvent = getNextEvent();
//_logger.info("step: " + currentEvent);
switch (currentEvent.event)
}
+ /**
+ * Has finished reading the stream?
+ */
private boolean hasFinished = false;
+ /**
+ * Is next event available?
+ *
+ * @return true if there are remaining events, otherwise fales
+ * @throws XerialException
+ */
private boolean hasNext() throws XerialException
{
if (eventQueue.isEmpty())
return true;
}
- private TreeEvent next() throws XerialException
+ /**
+ * Retrieves the next event from the queue. If the event queue is empty,
+ * fill the queue with the next event
+ *
+ * @return the next event.
+ * @throws XerialException
+ */
+ private TreeEvent getNextEvent() throws XerialException
{
if (!eventQueue.isEmpty())
return eventQueue.removeFirst();
if (hasFinished)
- return null;
+ throw new XerialError(XerialErrorCode.INVALID_STATE,
+ "hasNext() value must be checked before calling getNextEvent()");
fillQueue();
- return next();
+ return getNextEvent();
}
+ /**
+ * Fill the queue by retrieving the next event from the pull parser.
+ *
+ * @throws XerialException
+ */
private void fillQueue() throws XerialException
{
if (!parser.hasNext())
{
- closeUpTo(indentationOffset);
+ // no more input data
+ closeContextUpTo(indentationOffset);
hasFinished = true;
return;
}
case NODE:
// push context node
SilkNode newContextNode = SilkNode.class.cast(currentEvent.getElement());
- openContext(newContextNode, visitor);
+ openContext(newContextNode);
break;
case FUNCTION:
SilkFunction function = SilkFunction.class.cast(currentEvent.getElement());
- evalFunction(function, visitor);
+ evalFunction(function);
break;
case DATA_LINE:
- // pop the context stack up to the node with stream data node occurrence
+ // pop the context stack until finding a node for stream data node occurrence
while (!contextNodeStack.isEmpty())
{
SilkNode node = contextNodeStack.peekLast();
}
+ /**
+ * Plugin holder
+ */
private static Map<String, Class<SilkFunctionPlugin>> pluginTable = null;
+ /**
+ * Get the plugin of the specified name
+ *
+ * @param name
+ * plugin name
+ * @return plugin instance or null if no corresponding plugin is found.
+ */
private SilkFunctionPlugin getPlugin(String name)
{
Class<SilkFunctionPlugin> pluginClass = getPluginTable().get(name);
return pluginTable;
}
+ /**
+ * Information of the function (plugin) arguments (
+ * {@link SilkFunctionArgument}) described in the Class definition, which
+ * implements {@link SilkFunctionPlugin}.
+ *
+ * @author leo
+ *
+ */
private static class PluginField
{
Field field;
}
- private void populate(SilkFunctionPlugin plugin, List<SilkFunctionArg> args)
+ /**
+ * Fill the plug-in argument fields with the given arguments
+ *
+ * @param plugin
+ * plug-in instance.
+ * @param args
+ * function arguments.
+ */
+ private static void populate(SilkFunctionPlugin plugin, List<SilkFunctionArg> args)
{
PluginHolder holder = new PluginHolder(plugin.getClass());
holder.populate(plugin, args);
}
+ /**
+ * Get the context node
+ *
+ * @return
+ */
private SilkNode getContextNode()
{
if (contextNodeStack.isEmpty())
public XMLAttribute add(String attributeName, String attributeValue)
{
- _attributeNameList.add(attributeName);
- _attributeValue.put(attributeName, attributeValue);
+ String name = XMLGenerator.replaceWhiteSpaces(attributeName);
+ _attributeNameList.add(name);
+ _attributeValue.put(name, attributeValue);
return this;
}
Set< ? > keySet = properties.keySet();
for (Object attributeObj : keySet)
{
- String attribute = attributeObj.toString();
+ String attribute = XMLGenerator.replaceWhiteSpaces(attributeObj.toString());
String value = properties.getProperty(attribute);
this.add(attribute, value);
}
Set< ? > keySet = properties.keySet();
for (Object attributeObj : keySet)
{
- String attribute = attributeObj.toString();
+ String attribute = XMLGenerator.replaceWhiteSpaces(attributeObj.toString());
String value = properties.get(attribute).toString();
this.add(attribute, value);
}
return returnString;
}
- protected LinkedList<String> _attributeNameList = new LinkedList<String>();
- protected HashMap<String, String> _attributeValue = new HashMap<String, String>();
+ protected LinkedList<String> _attributeNameList = new LinkedList<String>();
+ protected HashMap<String, String> _attributeValue = new HashMap<String, String>();
}
}\r
}\r
\r
+ public static String replaceWhiteSpaces(String tagName)\r
+ {\r
+ return tagName.replaceAll("\\s+", "_");\r
+ }\r
+\r
public XMLGenerator startTag(String tagName, XMLAttribute attribute)\r
{\r
beforeStartTag();\r
\r
+ String tag = replaceWhiteSpaces(tagName);\r
+\r
_out.print("<");\r
- _out.print(tagName);\r
+ _out.print(tag);\r
\r
if (attribute != null && attribute.length() > 0)\r
{\r
_out.print(">");\r
\r
_currentLevel++;\r
- _tagStack.add(tagName);\r
+ _tagStack.add(tag);\r
_prevOut = PreviousOutput.StartTag;\r
\r
return this;\r
--- /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
+// SilkUtilTest.java\r
+// Since: 2009/02/14 5:57:08\r
+//\r
+// $URL$\r
+// $Author$\r
+//--------------------------------------\r
+package org.xerial.silk;\r
+\r
+import java.io.IOException;\r
+import java.io.StringReader;\r
+\r
+import org.junit.After;\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+import org.xerial.core.XerialException;\r
+import org.xerial.util.FileResource;\r
+import org.xerial.util.log.Logger;\r
+import org.xerial.util.xml.pullparser.PullParserUtil;\r
+import org.xmlpull.v1.XmlPullParser;\r
+import org.xmlpull.v1.XmlPullParserException;\r
+\r
+public class SilkUtilTest\r
+{\r
+ private static Logger _logger = Logger.getLogger(SilkUtilTest.class);\r
+\r
+ @Before\r
+ public void setUp() throws Exception\r
+ {}\r
+\r
+ @After\r
+ public void tearDown() throws Exception\r
+ {}\r
+\r
+ @Test\r
+ public void testToXML() throws IOException, XerialException, XmlPullParserException\r
+ {\r
+ String xml = SilkUtil.toXML(FileResource.find(SilkUtilTest.class, "small.silk"));\r
+ _logger.info(xml);\r
+\r
+ XmlPullParser pullParser = PullParserUtil.newParser(new StringReader(xml));\r
+\r
+ int event;\r
+ while ((event = pullParser.next()) != XmlPullParser.END_DOCUMENT)\r
+ {\r
+\r
+ }\r
+\r
+ }\r
+\r
+}\r