]> gitweb.fperrin.net Git - GpsPrune.git/blob - src/tim/prune/load/json/JsonBlock.java
Version 20.4, May 2021
[GpsPrune.git] / src / tim / prune / load / json / JsonBlock.java
1 package tim.prune.load.json;
2
3 import java.util.ArrayList;
4
5 /**
6  * Structure to hold the contents of a Json block during parsing.
7  * This will be held within the current [] or {} block
8  */
9 public class JsonBlock
10 {
11         private String _latitude = null;
12         private String _longitude = null;
13         private String _altitude = null;
14
15         private Expectation _nextField = Expectation.NONE;
16         private BlockType _type = BlockType.NONE;
17         private boolean _hasNonNumbers = false;
18         private ArrayList<String> _pointCoords = null;
19         private ArrayList<ArrayList<String>> _coordList = null;
20
21
22         private enum BlockType {
23                 NONE, FIELDS, POINTCOORDS, ISPOINTLIST, HASPOINTLIST
24         }
25         private enum Expectation {
26                 NONE, LATITUDE, LONGITUDE, ALTITUDE, COORDS
27         }
28
29         /** Internal method to remove quotes and NaNs from altitude strings */
30         private String modifyAltitudeString(String inAlt)
31         {
32                 if (inAlt == null || inAlt.equals("") || inAlt.equals("\"\"")) {
33                         return null;
34                 }
35                 String result = inAlt;
36                 if (inAlt.length() > 2 && inAlt.startsWith("\"") && inAlt.endsWith("\""))
37                 {
38                         result = inAlt.substring(1, inAlt.length()-1);
39                 }
40                 if (result.equals("NaN")) {return null;}
41                 return result;
42         }
43
44         /**
45          * Receive a token to this block
46          * @param inToken token from Json source
47          */
48         public void addToken(String inToken)
49         {
50                 if (inToken == null || inToken.isEmpty()) {return;}
51                 if (!_hasNonNumbers && !looksLikeNumber(inToken)) {
52                         _hasNonNumbers = true;
53                 }
54                 if (inToken.equals("\"latitude\"")) {
55                         _nextField = Expectation.LATITUDE;
56                 }
57                 else if (inToken.equals("\"longitude\"")) {
58                         _nextField = Expectation.LONGITUDE;
59                 }
60                 else if (inToken.equals("\"altitude\"")) {
61                         _nextField = Expectation.ALTITUDE;
62                 }
63                 else if (inToken.equals("\"coordinates\"")) {
64                         _nextField = Expectation.COORDS;
65                 }
66                 else
67                 {
68                         switch (_nextField)
69                         {
70                                 case LATITUDE:
71                                         _latitude = inToken;
72                                         _type = BlockType.FIELDS;
73                                         break;
74                                 case LONGITUDE:
75                                         _longitude = inToken;
76                                         _type = BlockType.FIELDS;
77                                         break;
78                                 case ALTITUDE:
79                                         _altitude = modifyAltitudeString(inToken);
80                                         _type = BlockType.FIELDS;
81                                         break;
82                                 default:
83                                         if (!_hasNonNumbers && looksLikeNumber(inToken))
84                                         {
85                                                 if (_pointCoords == null) {
86                                                         _pointCoords = new ArrayList<String>();
87                                                 }
88                                                 _pointCoords.add(inToken);
89                                                 _type = BlockType.POINTCOORDS;
90                                         }
91                                         break;
92                         }
93                         _nextField = Expectation.NONE;
94                 }
95         }
96
97         /** @return true if String can be parsed as a double */
98         private static boolean looksLikeNumber(String inToken)
99         {
100                 double value = Double.NaN;
101                 try {
102                         value = Double.parseDouble(inToken);
103                 }
104                 catch (NumberFormatException nfe) {}
105                 return !Double.isNaN(value);
106         }
107
108         /** @return true if named fields are valid */
109         public boolean areFieldsValid() {
110                 return _type == BlockType.FIELDS && _latitude != null && _longitude != null;
111         }
112
113         /** @return true if list of 2 or 3 doubles for a single point is valid */
114         public boolean areSingleCoordsValid()
115         {
116                 return !_hasNonNumbers && _pointCoords != null
117                         && _pointCoords.size() >= 2 && _pointCoords.size() <= 3;
118         }
119
120         /**
121          * @param inNewSegment new segment flag
122          * @return created point
123          */
124         public JsonPoint createSinglePoint(boolean inNewSegment)
125         {
126                 return new JsonPoint(_latitude, _longitude, _altitude, inNewSegment);
127         }
128
129         /**
130          * Child block has finished processing a single set of point coordinates
131          * @param inChild child block
132          */
133         public void addSingleCoordsFrom(JsonBlock inChild)
134         {
135                 if (_type == BlockType.NONE && _nextField == Expectation.COORDS
136                         && inChild._type == BlockType.POINTCOORDS)
137                 {
138                         // Named coordinates field
139                         _type = BlockType.FIELDS;
140                         _longitude = inChild.getSinglePointCoords(0);
141                         _latitude = inChild.getSinglePointCoords(1);
142                         _altitude = inChild.getSinglePointCoords(2);
143                 }
144                 else if ((_type == BlockType.NONE || _type == BlockType.ISPOINTLIST)
145                         && !_hasNonNumbers && _nextField == Expectation.NONE
146                         && inChild._type == BlockType.POINTCOORDS)
147                 {
148                         // add coordinates to list
149                         _type = BlockType.ISPOINTLIST;
150                         if (_coordList == null) {
151                                 _coordList = new ArrayList<ArrayList<String>>();
152                         }
153                         _coordList.add(inChild._pointCoords);
154                 }
155         }
156
157         /** @return point coordinate for given index, or null if not present */
158         private String getSinglePointCoords(int inIndex)
159         {
160                 if (_pointCoords == null || inIndex < 0 || inIndex >= _pointCoords.size()) {
161                         return null;
162                 }
163                 return _pointCoords.get(inIndex);
164         }
165
166         /** @return true if list of point coords is valid */
167         public boolean isCoordListValid()
168         {
169                 return !_hasNonNumbers && _type == BlockType.ISPOINTLIST
170                         && _coordList != null;
171         }
172
173         /** @return true if this block has a valid list of point coords */
174         public boolean hasValidCoordList()
175         {
176                 return _type == BlockType.HASPOINTLIST && _coordList != null;
177         }
178
179         /**
180          * Child block has finished processing a list of point coordinates
181          * @param inChild child block
182          */
183         public void addCoordListFrom(JsonBlock inChild)
184         {
185                 if (_type == BlockType.NONE && _nextField == Expectation.COORDS
186                         && inChild._type == BlockType.ISPOINTLIST)
187                 {
188                         _coordList = inChild._coordList;
189                         _type = BlockType.HASPOINTLIST;
190                 }
191                 else if ((_type == BlockType.NONE || _type == BlockType.ISPOINTLIST)
192                         && !_hasNonNumbers && inChild._type == BlockType.ISPOINTLIST)
193                 {
194                         if (_coordList == null) {
195                                 _coordList = new ArrayList<ArrayList<String>>();
196                         }
197                         _coordList.addAll(inChild._coordList);
198                         _type = BlockType.ISPOINTLIST;
199                 }
200         }
201
202         /**
203          * @return number of points in the list, if this block has a list
204          */
205         public int getNumPoints()
206         {
207                 if (hasValidCoordList()) {
208                         return _coordList.size();
209                 }
210                 return 0;
211         }
212
213         /**
214          * @param inIndex point index within list
215          * @return created point for specified index
216          */
217         public JsonPoint createPointFromList(int inIndex)
218         {
219                 if (inIndex < 0 || inIndex >= getNumPoints()) {return null;}
220                 ArrayList<String> pointCoords = _coordList.get(inIndex);
221                 if (pointCoords.size() < 2 || pointCoords.size() > 3) {return null;}
222                 final String longitude = pointCoords.get(0);
223                 final String latitude = pointCoords.get(1);
224                 final String altitude = ((pointCoords.size() == 3) ? pointCoords.get(3) : null);
225                 return new JsonPoint(latitude, longitude, altitude, inIndex == 0);
226         }
227 }