com.davidbracewell.io.structured.StructuredReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mango Show documentation
Show all versions of mango Show documentation
A set of utilities and tools to speed up and ease programming in Java.
/*
* (c) 2005 David B. Bracewell
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.davidbracewell.io.structured;
import com.davidbracewell.conversion.Cast;
import com.davidbracewell.conversion.Val;
import com.davidbracewell.reflection.BeanMap;
import com.davidbracewell.reflection.Reflect;
import com.davidbracewell.reflection.ReflectionException;
import com.davidbracewell.string.StringUtils;
import com.davidbracewell.tuple.Tuple2;
import com.google.common.base.Supplier;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.NonNull;
import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* Represents a class for reading data in a structured format, e.g. xml, json, yaml, etc. Individual implementations
* may
* provide extra functionality (e.g. read xml attributes).
*
* @author David B. Bracewell
*/
public abstract class StructuredReader implements Closeable {
/**
* Gets document type.
*
* @return the document type
*/
public abstract ElementType getDocumentType();
/**
* Begins an Array
*
* @return This array's name
* @throws IOException Something went wrong reading
*/
public abstract String beginArray() throws IOException;
/**
* Begins an array with an expected name.
*
* @param expectedName The name that the next array should have
* @return the structured reader
* @throws IOException Something happened reading or the expected name was not found
*/
public final StructuredReader beginArray(String expectedName) throws IOException {
String name = beginArray();
if (!StringUtils.isNullOrBlank(expectedName) && (name == null || !name.equals(expectedName))) {
throw new IOException("Expected " + expectedName);
}
return this;
}
/**
* Begins the document
*
* @return This structured writer
* @throws IOException Something went wrong reading
*/
public abstract StructuredReader beginDocument() throws IOException;
/**
* Begins the document
*
* @return The object's name
* @throws IOException Something went wrong reading
*/
public abstract String beginObject() throws IOException;
/**
* Begins an object with an expected name.
*
* @param expectedName The name that the next object should have
* @return the structured reader
* @throws IOException Something happened reading or the expected name was not found
*/
public final StructuredReader beginObject(String expectedName) throws IOException {
String name = beginObject();
if (!StringUtils.isNullOrBlank(expectedName) && !name.equals(expectedName)) {
throw new IOException("Expected " + expectedName);
}
return this;
}
/**
* Ends an Array
*
* @return the structured reader
* @throws IOException Something went wrong reading
*/
public abstract StructuredReader endArray() throws IOException;
/**
* Ends the document
*
* @throws IOException Something went wrong reading
*/
public abstract void endDocument() throws IOException;
/**
* Ends the document
*
* @return the structured reader
* @throws IOException Something went wrong reading
*/
public abstract StructuredReader endObject() throws IOException;
/**
* Checks if there is something left to read
*
* @return True if there is something in the stream to read
* @throws IOException Something went wrong reading
*/
public abstract boolean hasNext() throws IOException;
/**
* Reads the next array and returns a list of its values
*
* @return A list of the values in the array
* @throws IOException Something went wrong reading the array
*/
public final Val[] nextArray() throws IOException {
return nextArray(StringUtils.EMPTY);
}
/**
* Next array t [ ].
*
* @param the type parameter
* @param elementType the element type
* @return the t [ ]
* @throws IOException the io exception
*/
public final T[] nextArray(@NonNull Class elementType) throws IOException {
return nextArray(StringUtils.EMPTY, elementType);
}
/**
* Reads the next array with an expected name and returns a list of its values
*
* @param expectedName The name that the next array should have
* @return A list of the values in the array
* @throws IOException Something went wrong reading the array or the expected name was not found
*/
public final Val[] nextArray(String expectedName) throws IOException {
beginArray(expectedName);
List array = Lists.newArrayList();
while (peek() != ElementType.END_ARRAY) {
array.add(nextValue());
}
endArray();
return array.toArray(new Val[array.size()]);
}
/**
* Next array t [ ].
*
* @param the type parameter
* @param expectedName the expected name
* @param elementType the element type
* @return the t [ ]
* @throws IOException the io exception
*/
public final T[] nextArray(String expectedName, @NonNull Class elementType) throws IOException {
beginArray(expectedName);
List array = Lists.newArrayList();
while (peek() != ElementType.END_ARRAY) {
array.add(nextValue(elementType));
}
endArray();
return array.toArray(Cast.as(Array.newInstance(elementType, array.size())));
}
/**
* Next collection t.
*
* @param the type parameter
* @param supplier the supplier
* @return the t
* @throws IOException the io exception
*/
public > T nextCollection(@NonNull Supplier supplier) throws IOException {
return nextCollection(supplier, StringUtils.EMPTY);
}
/**
* Next collection t.
*
* @param the type parameter
* @param the type parameter
* @param supplier the supplier
* @param elementType the element type
* @return the t
* @throws IOException the io exception
*/
public , R> T nextCollection(@NonNull Supplier supplier, @NonNull Class elementType) throws IOException {
return nextCollection(supplier, null, elementType);
}
/**
* Next collection t.
*
* @param the type parameter
* @param supplier the supplier
* @param expectedName the expected name
* @return the t
* @throws IOException the io exception
*/
public > T nextCollection(@NonNull Supplier supplier, String expectedName) throws IOException {
beginArray(expectedName);
T collection = supplier.get();
while (peek() != ElementType.END_ARRAY) {
collection.add(nextValue());
}
endArray();
return collection;
}
/**
* Next collection t.
*
* @param the type parameter
* @param the type parameter
* @param supplier the supplier
* @param expectedName the expected name
* @param elementType the element type
* @return the t
* @throws IOException the io exception
*/
public , R> T nextCollection(@NonNull Supplier supplier, String expectedName, @NonNull Class elementType) throws IOException {
beginArray(expectedName);
T collection = supplier.get();
while (peek() != ElementType.END_ARRAY) {
collection.add(nextValue(elementType));
}
endArray();
return collection;
}
/**
* Next key value tuple 2.
*
* @return The next key value Tuple2
* @throws IOException Something went wrong reading
*/
public abstract Tuple2 nextKeyValue() throws IOException;
/**
* Next key value tuple 2.
*
* @param the type parameter
* @param clazz the clazz
* @return the tuple 2
* @throws IOException the io exception
*/
public abstract Tuple2 nextKeyValue(Class clazz) throws IOException;
/**
* Reads in a key value with an expected key.
*
* @param expectedKey The expected key
* @return The next key value Tuple2
* @throws IOException Something went wrong reading
*/
public final Val nextKeyValue(String expectedKey) throws IOException {
Tuple2 Tuple2 = nextKeyValue();
if (expectedKey != null && (Tuple2 == null || !Tuple2.getKey().equals(expectedKey))) {
throw new IOException("Expected a Key-Value Tuple2 with named " + expectedKey);
}
return Tuple2.getV2();
}
/**
* Next key value t.
*
* @param the type parameter
* @param expectedKey the expected key
* @param clazz the clazz
* @return the t
* @throws IOException the io exception
*/
public final T nextKeyValue(String expectedKey, Class clazz) throws IOException {
Tuple2 Tuple2 = nextKeyValue(clazz);
if (expectedKey != null && (Tuple2 == null || !Tuple2.getKey().equals(expectedKey))) {
throw new IOException("Expected a Key-Value Tuple2 with named " + expectedKey);
}
return Tuple2.getV2();
}
/**
* Reads the next value
*
* @return The next value
* @throws IOException Something went wrong reading
*/
public final Val nextValue() throws IOException {
switch (peek()) {
case BEGIN_ARRAY:
return Val.of(nextCollection(ArrayList::new));
case BEGIN_OBJECT:
return Val.of(nextMap());
case NAME:
return nextKeyValue().getV2();
default:
return nextSimpleValue();
}
}
/**
* Next simple value val.
*
* @return the val
* @throws IOException the io exception
*/
protected abstract Val nextSimpleValue() throws IOException;
private T readReadable(Class clazz) throws IOException {
try {
T object = Reflect.onClass(clazz).create().get();
boolean objectWrapped = peek() == ElementType.BEGIN_OBJECT;
if (objectWrapped) beginObject();
Cast.as(object).read(this);
if (objectWrapped) endObject();
return object;
} catch (ReflectionException e) {
throw new IOException(e);
}
}
/**
* Reads the next value
*
* @param the type parameter
* @param clazz the clazz
* @return The next value
* @throws IOException Something went wrong reading
*/
public final T nextValue(@NonNull Class clazz) throws IOException {
if (Readable.class.isAssignableFrom(clazz)) {
return readReadable(clazz);
} else if (peek() == ElementType.BEGIN_OBJECT) {
Reflect reflected = Reflect.onClass(clazz);
Optional staticRead = reflected.getMethods("read", 1).stream()
.filter(m -> StructuredReader.class.isAssignableFrom(m.getParameterTypes()[0]))
.filter(m -> Modifier.isStatic(m.getModifiers()))
.findFirst();
if (staticRead.isPresent()) {
try {
beginObject();
T result = Cast.as(staticRead.get().invoke(null, this));
endObject();
return result;
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IOException(e);
}
}
try {
T object = Reflect.onClass(clazz).create().get();
beginObject();
new BeanMap(object).putAll(nextMap());
endObject();
return object;
} catch (ReflectionException e) {
throw new IOException(e);
}
}
return nextValue().as(clazz);
}
/**
* Examines the type of the next element in the stream without consuming it.
*
* @return The type of the next element in the stream
* @throws IOException Something went wrong reading
*/
public abstract ElementType peek() throws IOException;
/**
* Reads an object (but does not beginObject() or endObject()) to a map
*
* @return A map of keys and values within an object
* @throws IOException Something went wrong reading
*/
public Map nextMap() throws IOException {
return nextMap(StringUtils.EMPTY);
}
/**
* Next map map.
*
* @param expectedName the expected name
* @return the map
* @throws IOException the io exception
*/
public Map nextMap(String expectedName) throws IOException {
boolean ignoreObject = peek() != ElementType.BEGIN_OBJECT && StringUtils.isNullOrBlank(expectedName);
if (!ignoreObject) beginObject(expectedName);
Map map = Maps.newHashMap();
while (peek() != ElementType.END_OBJECT) {
Tuple2 kv = nextKeyValue();
map.put(kv.getKey(), kv.getValue());
}
if (!ignoreObject) endObject();
return map;
}
/**
* Next map map.
*
* @param the type parameter
* @param valueType the value type
* @return the map
* @throws IOException the io exception
*/
public Map nextMap(@NonNull Class valueType) throws IOException {
return nextMap(null, valueType);
}
/**
* Next map map.
*
* @param the type parameter
* @param expectedName the expected name
* @param valueType the value type
* @return the map
* @throws IOException the io exception
*/
public Map nextMap(String expectedName, @NonNull Class valueType) throws IOException {
boolean ignoreObject = peek() != ElementType.BEGIN_OBJECT && expectedName == null;
if (!ignoreObject) beginObject(expectedName);
Map map = Maps.newHashMap();
while (peek() != ElementType.END_OBJECT) {
Tuple2 kv = nextKeyValue(valueType);
map.put(kv.getKey(), kv.getValue());
}
if (!ignoreObject) endObject();
return map;
}
/**
* Skips the next element in the stream
*
* @return The type of the element that was skipped
* @throws IOException Something went wrong reading
*/
public abstract ElementType skip() throws IOException;
}//END OF StructuredReader
© 2015 - 2025 Weber Informatics LLC | Privacy Policy