blob: 3251dd83921160d1791db74ebe2726e6e3ec09dc [file] [log] [blame]
/*********************************************************************
* Copyright (c) 2017 Boeing
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Boeing - initial API and implementation
**********************************************************************/
package org.eclipse.osee.framework.core.util;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.function.Function;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.osee.framework.core.data.ApplicabilityId;
import org.eclipse.osee.framework.core.data.ApplicabilityToken;
import org.eclipse.osee.framework.core.data.ArtifactId;
import org.eclipse.osee.framework.core.data.ArtifactToken;
import org.eclipse.osee.framework.core.data.TransactionId;
import org.eclipse.osee.framework.core.data.TransactionToken;
import org.eclipse.osee.framework.core.data.TransactionTokenDeserializer;
import org.eclipse.osee.framework.core.data.TransactionTokenSerializer;
import org.eclipse.osee.framework.core.data.UserToken;
import org.eclipse.osee.framework.core.data.UserTokenDeserializer;
import org.eclipse.osee.framework.core.data.UserTokenSerializer;
import org.eclipse.osee.framework.core.enums.BranchState;
import org.eclipse.osee.framework.core.enums.BranchType;
import org.eclipse.osee.framework.jdk.core.type.Id;
import org.eclipse.osee.framework.jdk.core.type.IdDeserializer;
import org.eclipse.osee.framework.jdk.core.type.IdSerializer;
import org.eclipse.osee.framework.jdk.core.type.NamedIdDeserializer;
import org.eclipse.osee.framework.jdk.core.type.OseeCoreException;
/**
* Since both ObjectReader / ObjectWriter are immutable, methods will create new readers/writers and configuration is
* fully thread-safe. Cost of these instances is minimal (unlike cost of creating ObjectMappers, which is substantial)
* <a href="https://github.com/FasterXML/jackson-databind/wiki/JacksonFeature">Jackson Documentation</a> <br />
* <br />
* ObjectMappers is thread safe but cost of new instances is substantial so we will share an instance. Callers are
* prohibited from changing its configuration <a href=
* "https://stackoverflow.com/questions/3907929/should-i-declare-jacksons-objectmapper-as-a-static-field">Stackoverflow
* Ref</a>
*
* @author Ryan D. Brooks
*/
public class JsonUtil {
private static ObjectMapper mapper = createStandardDateObjectMapper(createModule());
private static ObjectMapper mapper2 =
createObjectMapper(createModule()).setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm a z"));
public static ObjectMapper getMapper() {
return mapper;
}
public static JsonFactory getFactory() {
return mapper2.getFactory();
}
public static JsonNode readTree(String json) {
return readTree(mapper, json);
}
public static JsonNode readTree(ObjectMapper mapper, String json) {
try {
return mapper.readTree(json);
} catch (IOException ex) {
throw OseeCoreException.wrap(ex);
}
}
public static String toJson(Object object) {
return toJson(mapper, object);
}
public static String toJson(ObjectMapper mapper, Object object) {
try {
return mapper.writeValueAsString(object);
} catch (JsonProcessingException ex) {
throw OseeCoreException.wrap(ex);
}
}
public static <T> T readValue(String content, Class<T> valueType) {
return readValue(mapper, content, valueType);
}
public static <T> T readValue(ObjectMapper mapper, String content, Class<T> valueType) {
try {
return mapper.readValue(content, valueType);
} catch (IOException ex) {
throw OseeCoreException.wrap(ex);
}
}
public static <T> T readValue(String json, TypeReference<Map<String, String>> typeReference) {
return readValue(mapper, json, typeReference);
}
public static <T> T readValue(ObjectMapper mapper, String json, TypeReference<Map<String, String>> typeReference) {
try {
return (T) mapper.readValue(json, typeReference);
} catch (IOException ex) {
throw OseeCoreException.wrap(ex);
}
}
/**
* @param array must be a Json array of Json objects
* @param expectedName the value of the "Name" field
* @return the array element (JsonNode obj) named expectedName
*/
public static JsonNode getArrayElement(JsonNode array, String key, String value) {
for (JsonNode element : array) {
//
JsonNode node = element.get(key);
if (node != null) {
if (value.equals(node.asText())) {
return element;
}
}
}
return null;
}
public static JsonNode getJsonParserTree(JsonParser jp) {
try {
return jp.getCodec().readTree(jp);
} catch (Exception ex) {
throw OseeCoreException.wrap(ex);
}
}
public static boolean hasAnnotation(Class<? extends Annotation> toMatch, Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation.annotationType().isAssignableFrom(toMatch)) {
return true;
}
}
return false;
}
public static ObjectMapper createStandardDateObjectMapper(Module module) {
return createObjectMapper(module).setDateFormat(new SimpleDateFormat("MMM d, yyyy h:mm:ss aa"));
}
public static <T extends Id> void addDeserializer(SimpleModule module, Class<T> clazz, Function<Long, T> creator) {
module.addDeserializer(clazz, new IdDeserializer<>(clazz, creator));
}
public static SimpleModule createModule() {
SimpleModule module = new SimpleModule("OSEE", new Version(1, 0, 0, "", "", ""));
addDeserializer(module, ArtifactId.class, ArtifactId::valueOf);
addDeserializer(module, TransactionId.class, TransactionId::valueOf);
addDeserializer(module, Id.class, Id::valueOf);
addDeserializer(module, ApplicabilityId.class, ApplicabilityId::valueOf);
module.addDeserializer(ApplicabilityToken.class, new NamedIdDeserializer<>(ApplicabilityToken::valueOf));
module.addDeserializer(ArtifactToken.class,
new NamedIdDeserializer<@NonNull ArtifactToken>(ArtifactToken::valueOf));
module.addDeserializer(TransactionToken.class, new TransactionTokenDeserializer());
module.addDeserializer(UserToken.class, new UserTokenDeserializer());
module.addSerializer(TransactionToken.class, new TransactionTokenSerializer());
module.addSerializer(UserToken.class, new UserTokenSerializer());
JsonSerializer<@NonNull Id> idSerializer = new IdSerializer();
module.addSerializer(TransactionId.class, idSerializer);
module.addSerializer(BranchType.class, idSerializer);
module.addSerializer(BranchState.class, idSerializer);
module.addSerializer(ApplicabilityId.class, idSerializer);
module.addSerializer(java.util.Date.class, DateSerializer.instance);
return module;
}
private static ObjectMapper createObjectMapper(Module module) {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, false);
objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
objectMapper.configure(MapperFeature.AUTO_DETECT_CREATORS, true);
objectMapper.configure(MapperFeature.AUTO_DETECT_FIELDS, true);
objectMapper.configure(MapperFeature.AUTO_DETECT_FIELDS, true);
objectMapper.configure(MapperFeature.AUTO_DETECT_GETTERS, true);
objectMapper.configure(MapperFeature.AUTO_DETECT_IS_GETTERS, true);
objectMapper.configure(MapperFeature.AUTO_DETECT_SETTERS, true);
objectMapper.configure(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS, true);
objectMapper.configure(MapperFeature.USE_ANNOTATIONS, true);
objectMapper.configure(MapperFeature.USE_ANNOTATIONS, true);
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
objectMapper.configure(SerializationFeature.WRAP_EXCEPTIONS, true);
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
objectMapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS, true);
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\n"));
objectMapper.setDefaultPrettyPrinter(prettyPrinter);
return objectMapper;
}
}