package tim.prune.load.json; import java.util.ArrayList; /** * Structure to hold the contents of a Json block during parsing. * This will be held within the current [] or {} block */ public class JsonBlock { private String _latitude = null; private String _longitude = null; private String _altitude = null; private Expectation _nextField = Expectation.NONE; private BlockType _type = BlockType.NONE; private boolean _hasNonNumbers = false; private ArrayList _pointCoords = null; private ArrayList> _coordList = null; private enum BlockType { NONE, FIELDS, POINTCOORDS, ISPOINTLIST, HASPOINTLIST } private enum Expectation { NONE, LATITUDE, LONGITUDE, ALTITUDE, COORDS } /** Internal method to remove quotes and NaNs from altitude strings */ private String modifyAltitudeString(String inAlt) { if (inAlt == null || inAlt.equals("") || inAlt.equals("\"\"")) { return null; } String result = inAlt; if (inAlt.length() > 2 && inAlt.startsWith("\"") && inAlt.endsWith("\"")) { result = inAlt.substring(1, inAlt.length()-1); } if (result.equals("NaN")) {return null;} return result; } /** * Receive a token to this block * @param inToken token from Json source */ public void addToken(String inToken) { if (inToken == null || inToken.isEmpty()) {return;} if (!_hasNonNumbers && !looksLikeNumber(inToken)) { _hasNonNumbers = true; } if (inToken.equals("\"latitude\"")) { _nextField = Expectation.LATITUDE; } else if (inToken.equals("\"longitude\"")) { _nextField = Expectation.LONGITUDE; } else if (inToken.equals("\"altitude\"")) { _nextField = Expectation.ALTITUDE; } else if (inToken.equals("\"coordinates\"")) { _nextField = Expectation.COORDS; } else { switch (_nextField) { case LATITUDE: _latitude = inToken; _type = BlockType.FIELDS; break; case LONGITUDE: _longitude = inToken; _type = BlockType.FIELDS; break; case ALTITUDE: _altitude = modifyAltitudeString(inToken); _type = BlockType.FIELDS; break; default: if (!_hasNonNumbers && looksLikeNumber(inToken)) { if (_pointCoords == null) { _pointCoords = new ArrayList(); } _pointCoords.add(inToken); _type = BlockType.POINTCOORDS; } break; } _nextField = Expectation.NONE; } } /** @return true if String can be parsed as a double */ private static boolean looksLikeNumber(String inToken) { double value = Double.NaN; try { value = Double.parseDouble(inToken); } catch (NumberFormatException nfe) {} return !Double.isNaN(value); } /** @return true if named fields are valid */ public boolean areFieldsValid() { return _type == BlockType.FIELDS && _latitude != null && _longitude != null; } /** @return true if list of 2 or 3 doubles for a single point is valid */ public boolean areSingleCoordsValid() { return !_hasNonNumbers && _pointCoords != null && _pointCoords.size() >= 2 && _pointCoords.size() <= 3; } /** * @param inNewSegment new segment flag * @return created point */ public JsonPoint createSinglePoint(boolean inNewSegment) { return new JsonPoint(_latitude, _longitude, _altitude, inNewSegment); } /** * Child block has finished processing a single set of point coordinates * @param inChild child block */ public void addSingleCoordsFrom(JsonBlock inChild) { if (_type == BlockType.NONE && _nextField == Expectation.COORDS && inChild._type == BlockType.POINTCOORDS) { // Named coordinates field _type = BlockType.FIELDS; _longitude = inChild.getSinglePointCoords(0); _latitude = inChild.getSinglePointCoords(1); _altitude = inChild.getSinglePointCoords(2); } else if ((_type == BlockType.NONE || _type == BlockType.ISPOINTLIST) && !_hasNonNumbers && _nextField == Expectation.NONE && inChild._type == BlockType.POINTCOORDS) { // add coordinates to list _type = BlockType.ISPOINTLIST; if (_coordList == null) { _coordList = new ArrayList>(); } _coordList.add(inChild._pointCoords); } } /** @return point coordinate for given index, or null if not present */ private String getSinglePointCoords(int inIndex) { if (_pointCoords == null || inIndex < 0 || inIndex >= _pointCoords.size()) { return null; } return _pointCoords.get(inIndex); } /** @return true if list of point coords is valid */ public boolean isCoordListValid() { return !_hasNonNumbers && _type == BlockType.ISPOINTLIST && _coordList != null; } /** @return true if this block has a valid list of point coords */ public boolean hasValidCoordList() { return _type == BlockType.HASPOINTLIST && _coordList != null; } /** * Child block has finished processing a list of point coordinates * @param inChild child block */ public void addCoordListFrom(JsonBlock inChild) { if (_type == BlockType.NONE && _nextField == Expectation.COORDS && inChild._type == BlockType.ISPOINTLIST) { _coordList = inChild._coordList; _type = BlockType.HASPOINTLIST; } else if ((_type == BlockType.NONE || _type == BlockType.ISPOINTLIST) && !_hasNonNumbers && inChild._type == BlockType.ISPOINTLIST) { if (_coordList == null) { _coordList = new ArrayList>(); } _coordList.addAll(inChild._coordList); _type = BlockType.ISPOINTLIST; } } /** * @return number of points in the list, if this block has a list */ public int getNumPoints() { if (hasValidCoordList()) { return _coordList.size(); } return 0; } /** * @param inIndex point index within list * @return created point for specified index */ public JsonPoint createPointFromList(int inIndex) { if (inIndex < 0 || inIndex >= getNumPoints()) {return null;} ArrayList pointCoords = _coordList.get(inIndex); if (pointCoords.size() < 2 || pointCoords.size() > 3) {return null;} final String longitude = pointCoords.get(0); final String latitude = pointCoords.get(1); final String altitude = ((pointCoords.size() == 3) ? pointCoords.get(3) : null); return new JsonPoint(latitude, longitude, altitude, inIndex == 0); } }