001package conexp.fx.core.xml;
002
003/*
004 * #%L
005 * Concept Explorer FX
006 * %%
007 * Copyright (C) 2010 - 2023 Francesco Kriegel
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025
026import java.util.Iterator;
027import java.util.List;
028import java.util.Map;
029
030import conexp.fx.core.collections.Collections3;
031
032public class Data<T> {
033
034  protected final Datatype type;
035  protected final String   key;
036  protected T              value;
037
038  public Data(final Datatype type, final String key, final T value) throws NullPointerException,
039      IndexOutOfBoundsException {
040    super();
041    if (type == null)
042      throw new NullPointerException("Unable to create data without type");
043    if (key == null)
044      throw new IndexOutOfBoundsException("Unable to create data without keys");
045//    if (value == null)
046//      throw new NullPointerException("Unable to create data without value");
047    this.type = type;
048    this.key = key;
049    this.value = value;
050  }
051
052  @Override
053  public boolean equals(Object object) {
054    if (object == null)
055      return false;
056    if (!(object instanceof Data))
057      return false;
058    final Data<?> other = (Data<?>) object;
059    if (!this.type.equals(other.type))
060      return false;
061    if (!this.key.equals(other.key))
062      return false;
063    if (!this.value.equals(other.value))
064      return false;
065    return true;
066  }
067
068  @Override
069  public int hashCode() {
070    return 1 + 2 * type.hashCode() + 3 * key.hashCode() + 5 * value.hashCode();
071  }
072
073  @Override
074  public String toString() {
075    return toString("");
076  }
077
078  private static final String PREFIX = "| ";
079
080  private final String toString(final String prefix) {
081    switch (type) {
082    case STRING:
083      return prefix + key + " (" + type + ") = \"" + value.toString() + "\"";
084    case BOOLEAN:
085    case INTEGER:
086    case BOOLEAN_LIST:
087    case INTEGER_LIST:
088    case STRING_LIST:
089      return prefix + key + " (" + type + ") = " + value.toString();
090    case METADATA:
091    case COMPOUND:
092      return prefix + key + " (" + type + ")\r\n";
093    case COMPOUND_LIST:
094      return prefix + key + " (" + type + ")"
095          + toString(prefix + PREFIX, this.toCompoundListData().getSubkey(), this.toCompoundListData().getValue());
096    default:
097      return prefix + key + " (" + type + ")\r\n";
098    }
099  }
100
101  private final String toString(final String prefix, final String subkey, final List<Map<String, Data<?>>> list) {
102    String string = "";
103    for (Map<String, Data<?>> map : list)
104      string += "\r\n" + prefix + subkey + "\r\n" + toString(prefix + PREFIX, map);
105    return string;
106  }
107
108  private final String toString(final String prefix, final Map<String, Data<?>> map) {
109    final Iterator<String> keys = Collections3.sort(map.keySet()).iterator();
110    String string = "";
111    if (keys.hasNext())
112      string += map.get(keys.next()).toString(prefix);
113    while (keys.hasNext())
114      string += "\r\n" + map.get(keys.next()).toString(prefix);
115    return string;
116  }
117
118  public final Datatype getType() {
119    return type;
120  }
121
122  public final String getKey() {
123    return new String(key);
124  }
125
126  public final T getValue() {
127    return value;
128  }
129
130  public final T setValue(T value) throws NullPointerException {
131    if (value == null)
132      throw new NullPointerException("Unable to set null value on data");
133    final T previous = this.value;
134    this.value = value;
135    return previous;
136  }
137
138  public final boolean isBooleanData() {
139    return getType().equals(Datatype.BOOLEAN);
140  }
141
142  public final BooleanData toBooleanData() throws ClassCastException {
143    if (!isBooleanData())
144      throw new ClassCastException("Cannot cast data of type " + type + " to " + Datatype.BOOLEAN);
145    return (BooleanData) this;
146  }
147
148  public final Boolean getBooleanValue() {
149    return toBooleanData().getValue();
150  }
151
152  public final void setBooleanValue(final Boolean value) {
153    toBooleanData().setValue(value);
154  }
155
156  public final boolean isIntegerData() {
157    return getType().equals(Datatype.INTEGER);
158  }
159
160  public final IntegerData toIntegerData() throws ClassCastException {
161    if (!isIntegerData())
162      throw new ClassCastException("Cannot cast data of type " + type + " to " + Datatype.INTEGER);
163    return (IntegerData) this;
164  }
165
166  public final Integer getIntegerValue() {
167    return toIntegerData().getValue();
168  }
169
170  public final void setIntegerValue(final Integer value) {
171    toIntegerData().setValue(value);
172  }
173
174  public final boolean isFloatData() {
175    return getType().equals(Datatype.FLOAT);
176  }
177
178  public final FloatData toFloatData() throws ClassCastException {
179    if (!isFloatData())
180      throw new ClassCastException("Cannot cast data of type " + type + " to " + Datatype.FLOAT);
181    return (FloatData) this;
182  }
183
184  public final Float getFloatValue() {
185    return toFloatData().getValue();
186  }
187
188  public final void setFloatValue(final Float value) {
189    toFloatData().setValue(value);
190  }
191
192  public final boolean isStringData() {
193    return getType().equals(Datatype.STRING);
194  }
195
196  public final StringData toStringData() throws ClassCastException {
197    if (!isStringData())
198      throw new ClassCastException("Cannot cast data of type " + type + " to " + Datatype.STRING);
199    return (StringData) this;
200  }
201
202  public final String getStringValue() {
203    return toStringData().getValue();
204  }
205
206  public final void setStringValue(final String value) {
207    toStringData().setValue(value);
208  }
209
210  public final boolean isCompoundData() {
211    return getType().equals(Datatype.COMPOUND);
212  }
213
214  public final CompoundData toCompoundData() throws ClassCastException {
215    if (!isCompoundData())
216      throw new ClassCastException("Cannot cast data of type " + type + " to " + Datatype.COMPOUND);
217    return (CompoundData) this;
218  }
219
220  public final Map<String, Data<?>> getCompoundValue() {
221    return toCompoundData().getValue();
222  }
223
224  public final boolean isListData() {
225    return isBooleanListData() || isIntegerListData() || isStringListData() || isCompoundListData();
226  }
227
228  public final ListData<?> toListData() throws ClassCastException {
229    if (!isListData())
230      throw new ClassCastException("Cannot cast data of type " + type + " to LIST");
231    return (ListData<?>) this;
232  }
233
234  public final List<?> getListValue() {
235    return toListData().getValue();
236  }
237
238  public final boolean isBooleanListData() {
239    return getType().equals(Datatype.BOOLEAN_LIST);
240  }
241
242  public final BooleanListData toBooleanListData() throws ClassCastException {
243    if (!isBooleanListData())
244      throw new ClassCastException("Cannot cast data of type " + type + " to " + Datatype.BOOLEAN_LIST);
245    return (BooleanListData) this;
246  }
247
248  public final List<Boolean> getBooleanListValue() {
249    return toBooleanListData().getValue();
250  }
251
252  public final boolean isIntegerListData() {
253    return getType().equals(Datatype.INTEGER_LIST);
254  }
255
256  public final IntegerListData toIntegerListData() throws ClassCastException {
257    if (!isIntegerListData())
258      throw new ClassCastException("Cannot cast data of type " + type + " to " + Datatype.INTEGER_LIST);
259    return (IntegerListData) this;
260  }
261
262  public final List<Integer> getIntegerListValue() {
263    return toIntegerListData().getValue();
264  }
265
266  public final boolean isFloatListData() {
267    return getType().equals(Datatype.FLOAT_LIST);
268  }
269
270  public final FloatListData toFloatListData() throws ClassCastException {
271    if (!isFloatListData())
272      throw new ClassCastException("Cannot cast data of type " + type + " to " + Datatype.FLOAT_LIST);
273    return (FloatListData) this;
274  }
275
276  public final List<Float> getFloatListValue() {
277    return toFloatListData().getValue();
278  }
279
280  public final boolean isStringListData() {
281    return getType().equals(Datatype.STRING_LIST);
282  }
283
284  public final StringListData toStringListData() throws ClassCastException {
285    if (!isStringListData())
286      throw new ClassCastException("Cannot cast data of type " + type + " to " + Datatype.STRING_LIST);
287    return (StringListData) this;
288  }
289
290  public final List<String> getStringListValue() {
291    return toStringListData().getValue();
292  }
293
294  public final boolean isCompoundListData() {
295    return getType().equals(Datatype.COMPOUND_LIST);
296  }
297
298  public final CompoundListData toCompoundListData() throws ClassCastException {
299    if (!isCompoundListData())
300      throw new ClassCastException("Cannot cast data of type " + type + " to " + Datatype.COMPOUND_LIST);
301    return (CompoundListData) this;
302  }
303
304  public final List<Map<String, Data<?>>> getCompoundListValue() {
305    return toCompoundListData().getValue();
306  }
307
308  public final boolean isMetadata() {
309    return getType().equals(Datatype.METADATA);
310  }
311
312  public final boolean isDocument() {
313    return getType().equals(Datatype.DOCUMENT);
314  }
315
316}