001/**
002 * Copyright 2014 The Kuali Foundation Licensed under the
003 * Educational Community License, Version 2.0 (the "License"); you may
004 * not use this file except in compliance with the License. You may
005 * obtain a copy of the License at
006 *
007 * http://www.osedu.org/licenses/ECL-2.0
008 *
009 * Unless required by applicable law or agreed to in writing,
010 * software distributed under the License is distributed on an "AS IS"
011 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
012 * or implied. See the License for the specific language governing
013 * permissions and limitations under the License.
014 *
015 * Created by Charles on 8/5/2014
016 */
017package org.kuali.student.poc.jsonparser.parser;
018
019import org.kuali.student.poc.jsonparser.json.BaseJsonObject;
020import org.kuali.student.poc.jsonparser.json.SimpleJsonBoolean;
021import org.kuali.student.poc.jsonparser.json.SimpleJsonList;
022import org.kuali.student.poc.jsonparser.json.SimpleJsonMap;
023import org.kuali.student.poc.jsonparser.json.SimpleJsonNull;
024import org.kuali.student.poc.jsonparser.json.SimpleJsonNumber;
025import org.kuali.student.poc.jsonparser.json.SimpleJsonString;
026import org.kuali.student.poc.jsonparser.tokenizer.SimpleJsonTokenizer;
027import org.kuali.student.poc.jsonparser.tokenizer.token.AtomToken;
028import org.kuali.student.poc.jsonparser.tokenizer.token.BaseToken;
029import org.kuali.student.poc.jsonparser.tokenizer.token.ConstantToken;
030import org.kuali.student.poc.jsonparser.tokenizer.token.NumberToken;
031import org.kuali.student.poc.jsonparser.tokenizer.token.StringToken;
032import org.kuali.student.poc.jsonparser.tokenizer.token.TokenConstants;
033
034import java.util.Iterator;
035import java.util.NoSuchElementException;
036
037/**
038 * Parses a simple JSON file
039 *
040 * @author Kuali Student Team
041 */
042public class SimpleJsonParser implements Iterable<BaseJsonObject> {
043    SimpleJsonTokenizer tokenizer;
044    String resourcePath;
045    public SimpleJsonParser(String resourcePath) {
046        tokenizer = new SimpleJsonTokenizer(resourcePath);
047        this.resourcePath = resourcePath;
048    }
049
050//    public List<BaseJsonObject> parse() throws MyJsonParseException {
051//        List<BaseJsonObject> jsonObjects = new ArrayList<>();
052//        try {
053//            Iterator<BaseToken> iterator = tokenizer.iterator();
054//            while (iterator.hasNext()) {
055//                BaseJsonObject jsonObject = recursiveParse(iterator);
056//                jsonObjects.add(jsonObject);
057//            }
058//        } catch (RuntimeException e) {
059//            return null;
060//        }
061//        return jsonObjects;
062//    }
063
064    private BaseJsonObject recursiveParse(Iterator<BaseToken> iterator) throws SimpleJsonParseException {
065        BaseToken token = iterator.next();
066        if (token instanceof AtomToken) {
067            AtomToken aToken = (AtomToken) token;
068            if (aToken.getTokenType().equals(TokenConstants.START_MAP_TOKEN)) {
069                SimpleJsonMap map = new SimpleJsonMap();
070                parseJsonMap(map, iterator);
071                return map;
072            } else if (aToken.getTokenType().equals(TokenConstants.START_LIST_TOKEN)) {
073                SimpleJsonList list = new SimpleJsonList();
074                parseJsonList(list, iterator);
075                return list;
076            }
077        } else if (token instanceof StringToken) {
078            StringToken sToken = (StringToken) token;
079            return new SimpleJsonString(sToken.getString());
080        } else if (token instanceof NumberToken) {
081            NumberToken nToken = (NumberToken) token;
082            return new SimpleJsonNumber(nToken.getValue());
083        } else if (token instanceof ConstantToken) {
084            ConstantToken cToken = (ConstantToken) token;
085            if (cToken == ConstantToken.TRUE_TOKEN) {
086                return SimpleJsonBoolean.TRUE;
087            } else if (cToken == ConstantToken.FALSE_TOKEN) {
088                return SimpleJsonBoolean.FALSE;
089            } else {
090                // Assume it must be null
091                return SimpleJsonNull.NULL;
092            }
093        }
094        throw new SimpleJsonParseException("Unknown token found");
095    }
096
097    private void parseJsonList(SimpleJsonList list, Iterator<BaseToken> iterator) throws SimpleJsonParseException {
098        BaseToken token = null;
099        while (!isEndListToken(token)) {
100            // Next token should be next element of list
101            BaseJsonObject element = recursiveParse(iterator);
102            list.add(element);
103            // Check for comma
104            token = iterator.next();
105            if (!isEndListToken(token)) {
106                if (!isCommaToken(token)) {
107                    throw new SimpleJsonParseException("Expecting a comma");
108                }
109            }
110        }
111    }
112
113    private void parseJsonMap(SimpleJsonMap map, Iterator<BaseToken> iterator) throws SimpleJsonParseException {
114        // At this point, the start brace
115        BaseToken token = iterator.next();
116        while (!isEndMapToken(token)) {
117            // Next token should be a key, which is a string token
118            StringToken sToken = (StringToken) token; // May class-cast
119            // Next token should be a colon
120            token = iterator.next();
121            if (!isColonToken(token)) {
122                throw new SimpleJsonParseException("Expecting a colon");
123            }
124            BaseJsonObject value = recursiveParse(iterator);
125            map.put(sToken.getString(), value);
126            // Check for comma
127            token = iterator.next();
128            if (!isEndMapToken(token)) {
129                if (!isCommaToken(token)) {
130                    throw new SimpleJsonParseException("Expecting a comma");
131                } else {
132                    // Get token after the comma
133                    token = iterator.next();
134                }
135            }
136        }
137    }
138
139    private boolean isEndMapToken(BaseToken token) {
140        if (token instanceof AtomToken) {
141            AtomToken aToken = (AtomToken) token;
142            return aToken.getTokenType().equals(TokenConstants.END_MAP_TOKEN);
143        }
144        return false;
145    }
146
147    private boolean isEndListToken(BaseToken token) {
148        if (token == null) {
149            return false;
150        }
151        if (token instanceof AtomToken) {
152            AtomToken aToken = (AtomToken) token;
153            return aToken.getTokenType().equals(TokenConstants.END_LIST_TOKEN);
154        }
155        return false;
156    }
157
158    private boolean isColonToken(BaseToken token) {
159        if (token instanceof AtomToken) {
160            AtomToken aToken = (AtomToken) token;
161            return aToken.getTokenType().equals(TokenConstants.COLON_TOKEN);
162        }
163        return false;
164    }
165
166    private boolean isCommaToken(BaseToken token) {
167        if (token instanceof AtomToken) {
168            AtomToken aToken = (AtomToken) token;
169            return aToken.getTokenType().equals(TokenConstants.COMMA_TOKEN);
170        }
171        return false;
172    }
173
174    @Override
175    public Iterator<BaseJsonObject> iterator() {
176        Iterator<BaseJsonObject> iter =
177                new Iterator<BaseJsonObject>() {
178                    SimpleJsonTokenizer tokenizer;
179                    Iterator<BaseToken> iterator;
180                    BaseJsonObject nextJsonObject;
181                    {
182                        tokenizer = new SimpleJsonTokenizer(resourcePath);
183                        iterator = tokenizer.iterator();
184                        nextJsonObject = getNextJsonObject();
185                    }
186
187                    private BaseJsonObject getNextJsonObject() {
188                        BaseJsonObject result = null;
189                        try {
190                            if (!iterator.hasNext()) {
191                                return null;
192                            }
193                            result = recursiveParse(iterator);
194                        } catch (SimpleJsonParseException e) {
195                            throw new RuntimeException("MyJsonParseException", e);
196                        }
197                        return result;
198                    }
199
200                    @Override
201                    public boolean hasNext() {
202                        return nextJsonObject != null;
203                    }
204
205                    @Override
206                    public BaseJsonObject next() {
207                        BaseJsonObject saved = nextJsonObject;
208                        if (saved == null) {
209                            throw new NoSuchElementException();
210                        } else {
211                            nextJsonObject = getNextJsonObject();
212                        }
213
214                        return saved;
215                    }
216
217                    @Override
218                    public void remove() {
219                        throw new UnsupportedOperationException("remove not supported");
220                    }
221                };
222        return iter;
223    }
224}