+++ /dev/null
-/*\r
- * JSON string output\r
- *\r
- * Copyright(c) 2009 olyutorskii\r
- * $Id: JsonAppender.java 919 2009-11-24 14:32:50Z olyutorskii $\r
- */\r
-\r
-package jp.sourceforge.jindolf.json;\r
-\r
-import java.io.Flushable;\r
-import java.io.IOException;\r
-import java.util.Stack;\r
-\r
-/**\r
- * JSON文字出力用ビジター。\r
- * JSON Valueのトラバース時にこのビジターを指定すると、\r
- * 事前に用意した文字出力先にJSONフォーマットで出力される\r
- */\r
-class JsonAppender\r
- implements ValueVisitor,\r
- Flushable {\r
-\r
- private static final String NEWLINE = "\n";\r
- private static final String INDENT_UNIT = "\u0020\u0020";\r
- private static final String HASH_SEPARATOR = "\u0020:\u0020";\r
- private static final String ELEM_DELIMITOR = "\u0020,";\r
-\r
-\r
- private final Appendable appout;\r
-\r
- private final Stack<JsValue> valueStack = new Stack<JsValue>();\r
- private final Stack<Boolean> hasChildStack = new Stack<Boolean>();\r
-\r
- private boolean afterPairName = false;\r
-\r
-\r
- /**\r
- * コンストラクタ。\r
- * @param appout 出力先\r
- */\r
- public JsonAppender(Appendable appout){\r
- super();\r
- this.appout = appout;\r
- return;\r
- }\r
-\r
- /**\r
- * 1文字出力。\r
- * @param ch 文字\r
- * @throws IOException 出力エラー\r
- */\r
- protected void append(char ch) throws IOException{\r
- this.appout.append(ch);\r
- return;\r
- }\r
-\r
- /**\r
- * 文字列出力。\r
- * @param seq 文字列\r
- * @throws IOException 出力エラー\r
- */\r
- protected void append(CharSequence seq) throws IOException{\r
- this.appout.append(seq);\r
- return;\r
- }\r
-\r
- /**\r
- * 最後の改行を出力した後、可能であれば出力先をフラッシュする。\r
- * @throws IOException 出力エラー\r
- */\r
- public void flush() throws IOException{\r
- putNewLine();\r
- if(this.appout instanceof Flushable){\r
- ((Flushable)this.appout).flush();\r
- }\r
- return;\r
- }\r
-\r
- /**\r
- * 改行を出力する。\r
- * @throws IOException 出力エラー\r
- */\r
- protected void putNewLine()\r
- throws IOException{\r
- append(NEWLINE);\r
- return;\r
- }\r
-\r
- /**\r
- * インデントを出力する。\r
- * @throws IOException 出力エラー\r
- */\r
- protected void indentOut() throws IOException{\r
- int level = stackLength();\r
- for(int ct = 1; ct <= level; ct++){\r
- append(INDENT_UNIT);\r
- }\r
- return;\r
- }\r
-\r
- /**\r
- * 要素間区切りコンマを出力する。\r
- * JSONでは最後の要素の後にコンマを出力してはいけない。\r
- * @throws IOException 出力エラー\r
- */\r
- protected void putElemDelimitor()\r
- throws IOException{\r
- append(ELEM_DELIMITOR);\r
- return;\r
- }\r
-\r
- /**\r
- * pairの名前を出力する。\r
- * @param name pair名\r
- * @throws IOException 出力エラー\r
- */\r
- protected void putPairName(String name)\r
- throws IOException{\r
- JsString.writeText(this.appout, name);\r
- return;\r
- }\r
-\r
- /**\r
- * pair区切りコロンを出力する。\r
- * @throws IOException 出力エラー\r
- */\r
- protected void putPairSeparator()\r
- throws IOException{\r
- append(HASH_SEPARATOR);\r
- return;\r
- }\r
-\r
- /**\r
- * 一段ネストする。\r
- * @param value JSON Value\r
- * @throws IllegalArgumentException 引数がObjectでもArrayでもなかった\r
- */\r
- protected void pushValue(JsValue value)\r
- throws IllegalArgumentException{\r
- if( ! (value instanceof JsObject) && ! (value instanceof JsArray) ){\r
- throw new IllegalArgumentException();\r
- }\r
-\r
- this.valueStack.push(value);\r
- this.hasChildStack.push(false);\r
-\r
- return;\r
- }\r
-\r
- /**\r
- * ネストを一段解除する。\r
- * @return 最後にネストしていたValue\r
- */\r
- protected JsValue popValue(){\r
- this.hasChildStack.pop();\r
- return this.valueStack.pop();\r
- }\r
-\r
- /**\r
- * ネストのスタック段数を返す。\r
- * @return 段数\r
- */\r
- protected int stackLength(){\r
- return this.valueStack.size();\r
- }\r
-\r
- /**\r
- * ネスト後、一つでも子要素が出力されたか判定する。\r
- * @return 子要素が出力されていればtrue\r
- */\r
- protected boolean hasChildOut(){\r
- if(stackLength() <= 0) return false;\r
- return this.hasChildStack.peek();\r
- }\r
-\r
- /**\r
- * 現時点でのネストに対し、子要素が一つ以上出力済みであると設定する。\r
- */\r
- protected void setChildOut(){\r
- if(stackLength() <= 0) return;\r
- this.hasChildStack.pop();\r
- this.hasChildStack.push(true);\r
- }\r
-\r
- /**\r
- * {@inheritDoc}\r
- * Valueの出力を行う。\r
- * @param value {@inheritDoc}\r
- * @throws JsVisitException {@inheritDoc}\r
- */\r
- public void visitValue(JsValue value)\r
- throws JsVisitException{\r
- try{\r
- if( ! this.afterPairName ){\r
- if(hasChildOut()){\r
- putElemDelimitor();\r
- }\r
- putNewLine();\r
- indentOut();\r
- }\r
- this.afterPairName = false;\r
-\r
- setChildOut();\r
-\r
- if(value instanceof JsObject){\r
- append('{');\r
- pushValue(value);\r
- }else if(value instanceof JsArray){\r
- append('[');\r
- pushValue(value);\r
- }else{\r
- append(value.toString());\r
- }\r
- }catch(IOException e){\r
- throw new JsVisitException(e);\r
- }\r
-\r
- return;\r
- }\r
-\r
- /**\r
- * {@inheritDoc}\r
- * pairの名前を出力する。\r
- * @param name {@inheritDoc}\r
- * @throws JsVisitException {@inheritDoc}\r
- */\r
- public void visitPairName(String name)\r
- throws JsVisitException{\r
- try{\r
- if(hasChildOut()){\r
- putElemDelimitor();\r
- }\r
- putNewLine();\r
- indentOut();\r
- putPairName(name);\r
- putPairSeparator();\r
- }catch(IOException e){\r
- throw new JsVisitException(e);\r
- }\r
-\r
- setChildOut();\r
- this.afterPairName = true;\r
-\r
- return;\r
- }\r
-\r
- /**\r
- * {@inheritDoc}\r
- * 閉じ括弧を出力する。\r
- * @param composite {@inheritDoc}\r
- * @throws JsVisitException {@inheritDoc}\r
- */\r
- public void visitCollectionClose(JsValue composite)\r
- throws JsVisitException{\r
- boolean hasChild = hasChildOut();\r
-\r
- JsValue value = popValue();\r
-\r
- try{\r
- if(hasChild){\r
- putNewLine();\r
- indentOut();\r
- }else{\r
- append('\u0020');\r
- }\r
-\r
- if(value instanceof JsObject){\r
- append('}');\r
- }else if(value instanceof JsArray){\r
- append(']');\r
- }else{\r
- assert false;\r
- throw new JsVisitException();\r
- }\r
- }catch(IOException e){\r
- throw new JsVisitException(e);\r
- }\r
-\r
- return;\r
- }\r
-\r
-}\r