OSDN Git Service

start v1.101.107-SNAPSHOT
[jovsonz/Jovsonz.git] / src / main / java / jp / sourceforge / jovsonz / JsArray.java
1 /*
2  * JSON array value
3  *
4  * License : The MIT License
5  * Copyright(c) 2009 olyutorskii
6  */
7
8 package jp.sourceforge.jovsonz;
9
10 import java.io.IOException;
11 import java.util.Iterator;
12 import java.util.LinkedList;
13 import java.util.List;
14
15 /**
16  * JSON ARRAY型Valueを表す。
17  *
18  * <p>子要素の配列リストを反映する。
19  *
20  * <p>表記例
21  *
22  * <pre>
23  * [
24  *     true ,
25  *     "ABC" ,
26  *     12.3
27  * ]
28  * </pre>
29  */
30 public class JsArray
31         implements JsComposition<JsValue> {
32
33     private static final String ERRMSG_NOARRAYCOMMA =
34             "missing comma in ARRAY";
35     private static final String ERRMSG_NOELEM =
36             "missing element in ARRAY";
37
38     private final List<JsValue> valueList = new LinkedList<>();
39     private boolean changed = false;
40
41     /**
42      * コンストラクタ。
43      */
44     public JsArray(){
45         super();
46         return;
47     }
48
49     /**
50      * JSON文字列ソースからARRAY型Valueを読み込む。
51      *
52      * <p>さらに子Valueへとパース解析が進む可能性がある。
53      * 別型の可能性のある先頭文字を読み込んだ場合、
54      * ソースに文字を読み戻した後nullが返される。
55      *
56      * @param source 文字列ソース
57      * @return ARRAY型Value。別型の可能性がある場合はnull。
58      * @throws IOException 入力エラー
59      * @throws JsParseException 不正な表現または意図しない入力終了
60      */
61     static JsArray parseArray(JsonSource source)
62             throws IOException, JsParseException {
63         char charHead = source.readOrDie();
64         if(charHead != '['){
65             source.unread(charHead);
66             return null;
67         }
68
69         JsArray result = new JsArray();
70
71         for(;;){
72             source.skipWhiteSpace();
73             char chData = source.readOrDie();
74             if(chData == ']') break;
75
76             if(result.isEmpty()){
77                 source.unread(chData);
78             }else{
79                 if(chData != ','){
80                     throw new JsParseException(ERRMSG_NOARRAYCOMMA,
81                                                source.getLineNumber() );
82                 }
83             }
84
85             JsValue value = Json.parseValue(source);
86             if(value == null){
87                 throw new JsParseException(ERRMSG_NOELEM,
88                                            source.getLineNumber() );
89             }
90
91             result.add(value);
92         }
93
94         return result;
95     }
96
97     /**
98      * {@inheritDoc}
99      *
100      * <p>常に{@link JsTypes#ARRAY}を返す。
101      *
102      * @return {@inheritDoc}
103      */
104     @Override
105     public JsTypes getJsTypes(){
106         return JsTypes.ARRAY;
107     }
108
109     /**
110      * このValueおよび子孫に変更があったか判定する。
111      *
112      * <p>子要素の追加・削除が行われたか、
113      * もしくは子要素のいずれかに変更が認められれば、
114      * このARRAY型Valueに変更があったとみなされる。
115      *
116      * @return {@inheritDoc}
117      */
118     @Override
119     public boolean hasChanged(){
120         if(this.changed) return true;
121
122         for(JsValue value : this.valueList){
123             if( ! (value instanceof JsComposition) ) continue;
124             JsComposition<?> composition = (JsComposition) value;
125             if(composition.hasChanged()) return true;
126         }
127
128         return false;
129     }
130
131     /**
132      * このValueおよび子孫に変更がなかったことにする。
133      */
134     @Override
135     public void setUnchanged(){
136         this.changed = false;
137
138         for(JsValue value : this.valueList){
139             if( ! (value instanceof JsComposition) ) continue;
140             JsComposition<?> composition = (JsComposition) value;
141             composition.setUnchanged();
142         }
143
144         return;
145     }
146
147     /**
148      * 深さ優先探索を行い各種構造の出現をビジターに通知する。
149      *
150      * <p>thisを通知した後、子Valueを順に訪問し、最後に閉じ括弧を通知する。
151      *
152      * @param visitor {@inheritDoc}
153      * @throws JsVisitException {@inheritDoc}
154      */
155     @Override
156     public void traverse(ValueVisitor visitor) throws JsVisitException{
157         visitor.visitValue(this);
158
159         for(JsValue value : this.valueList){
160             value.traverse(visitor);
161         }
162
163         visitor.visitCompositionClose(this);
164
165         return;
166     }
167
168     /**
169      * 配列要素数を返す。
170      *
171      * @return {@inheritDoc}
172      */
173     @Override
174     public int size(){
175         return this.valueList.size();
176     }
177
178     /**
179      * 配列が空か判定する。
180      *
181      * @return {@inheritDoc}
182      */
183     @Override
184     public boolean isEmpty(){
185         return this.valueList.isEmpty();
186     }
187
188     /**
189      * 配列を空にする。
190      */
191     @Override
192     public void clear(){
193         if(this.valueList.size() > 0) this.changed = true;
194         this.valueList.clear();
195         return;
196     }
197
198     /**
199      * ハッシュ値を返す。
200      *
201      * <p>全ての子孫Valueのハッシュ値からその都度合成される。高コスト注意!。
202      *
203      * @return {@inheritDoc}
204      * @see java.util.List#hashCode()
205      */
206     @Override
207     public int hashCode(){
208         return this.valueList.hashCode();
209     }
210
211     /**
212      * 等価判定を行う。
213      *
214      * <p>双方の配列サイズが一致し
215      * その全ての子Valueでのequals()が等価と判断された場合のみ
216      * 等価と判断される。
217      *
218      * @param obj {@inheritDoc}
219      * @return {@inheritDoc}
220      * @see java.util.List#equals(Object)
221      */
222     @Override
223     public boolean equals(Object obj){
224         if(this == obj) return true;
225
226         if( ! (obj instanceof JsArray) ) return false;
227         JsArray array = (JsArray) obj;
228
229         return this.valueList.equals(array.valueList);
230     }
231
232     /**
233      * 配列にValueを追加する。
234      *
235      * <p>同じJsValueインスタンスを複数回追加することも可能。
236      *
237      * @param value JSON Value
238      * @throws NullPointerException 引数がnull
239      */
240     public void add(JsValue value) throws NullPointerException{
241         if(value == null) throw new NullPointerException();
242         this.valueList.add(value);
243         this.changed = true;
244         return;
245     }
246
247     /**
248      * 配列から指定された位置のValueを返す。
249      *
250      * @param index 0で始まる配列上の位置
251      * @return Value JSON Value
252      * @throws IndexOutOfBoundsException 不正な位置指定
253      */
254     public JsValue get(int index) throws IndexOutOfBoundsException{
255         return this.valueList.get(index);
256     }
257
258     /**
259      * 配列からValueを削除する。
260      *
261      * <p>{@link java.util.List#remove(Object)}と異なり、
262      * 削除対象の検索に際して
263      * {@link java.lang.Object#equals(Object)}は使われない。
264      *
265      * <p>一致するインスタンスが複数存在する場合、
266      * 先頭に近いインスタンスのみ削除される。
267      * 一致するインスタンスが存在しなければなにもしない。
268      *
269      * @param value JSON Value
270      * @return 既存のValueが削除されたならtrue
271      */
272     // TODO 必要?
273     public boolean remove(JsValue value){
274         boolean removed = false;
275
276         Iterator<JsValue> it = this.valueList.iterator();
277         while(it.hasNext()){
278             JsValue elem = it.next();
279             if(elem == value){
280                 it.remove();
281                 this.changed = true;
282                 removed = true;
283                 break;
284             }
285         }
286
287         return removed;
288     }
289
290     /**
291      * 配列から指定位置のValueを削除する。
292      *
293      * @param index 0で始まる削除対象のインデックス値
294      * @return 削除されたValue
295      * @throws IndexOutOfBoundsException 不正なインデックス値
296      */
297     public JsValue remove(int index) throws IndexOutOfBoundsException{
298         JsValue removed = this.valueList.remove(index);
299         this.changed = true;
300         return removed;
301     }
302
303     /**
304      * Valueにアクセスするための反復子を提供する。
305      *
306      * <p>この反復子での削除作業はできない。
307      *
308      * @return 反復子イテレータ
309      * @see UnmodIterator
310      */
311     @Override
312     public Iterator<JsValue> iterator(){
313         return UnmodIterator.unmodIterator(this.valueList);
314     }
315
316     /**
317      * {@inheritDoc}
318      *
319      * <p>文字列表現を返す。
320      *
321      * <p>JSON表記の全体もしくは一部としての利用も可能。
322      *
323      * @return {@inheritDoc}
324      */
325     @Override
326     public String toString(){
327         StringBuilder text = new StringBuilder();
328
329         text.append("[");
330         boolean hasElem = false;
331         for(JsValue value : this.valueList){
332             if(hasElem) text.append(',');
333             text.append(value);
334             hasElem = true;
335         }
336         text.append("]");
337
338         return text.toString();
339     }
340
341 }