Skip to content

Commit

Permalink
Merge pull request #15 from apache/feature/13-Implement-TypeSystemMod…
Browse files Browse the repository at this point in the history
…e.MINIMAL

Issue #13: Implement TypeSystemMode.MINIMAL
  • Loading branch information
reckart authored Jan 31, 2023
2 parents 3baaa83 + 203451f commit 112e561
Show file tree
Hide file tree
Showing 10 changed files with 345 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,40 @@
*/
package org.apache.uima.json.jsoncas2.model;

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;

import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.uima.cas.FeatureStructure;
import com.fasterxml.jackson.databind.DatabindContext;

public class FeatureStructures implements Iterable<FeatureStructure> {
public static final String ALL_FEATURE_STRUCTURES = "UIMA.AllFeatureStructures";

private final List<FeatureStructure> featureStructures;

private final Set<String> typeNames;

public FeatureStructures(Collection<FeatureStructure> aFeatureStructures) {
typeNames = new HashSet<>();
featureStructures = aFeatureStructures.stream() //
.sorted(Comparator.comparing(fs -> {
return fs.getType().getName();
})) //
.map(fs -> {
typeNames.add(fs.getType().getName());
return fs;
}) //
.sorted(comparing(fs -> fs.getType().getName())) //
.collect(toList());
}

public boolean existsAnnotationOfType(String aTypeName) {
return typeNames.contains(aTypeName);
}

@Override
public Iterator<FeatureStructure> iterator() {
return featureStructures.iterator();
Expand All @@ -46,4 +60,12 @@ public Iterator<FeatureStructure> iterator() {
public boolean isEmpty() {
return featureStructures.isEmpty();
}

public static void set(DatabindContext aProvider, FeatureStructures aAllFs) {
aProvider.setAttribute(ALL_FEATURE_STRUCTURES, aAllFs);
}

public static FeatureStructures get(DatabindContext aProvider) {
return (FeatureStructures) aProvider.getAttribute(ALL_FEATURE_STRUCTURES);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,12 @@ public void serialize(CAS aCas, JsonGenerator aJg, SerializerProvider aProvider)

serializeHeader(aCas, aJg, aProvider);

FeatureStructures allFSes = findAllFeatureStructures(aCas);
FeatureStructures.set(aProvider, allFSes);

serializeTypes(aCas, aJg, aProvider);

serializeFeatureStructures(aCas, aJg, aProvider);
serializeFeatureStructures(allFSes, aJg, aProvider);

serializeViews(aCas, aJg, aProvider);

Expand All @@ -94,13 +97,12 @@ private void serializeTypes(CAS aCas, JsonGenerator aJg, SerializerProvider aPro
}
}

private void serializeFeatureStructures(CAS aCas, JsonGenerator aJg, SerializerProvider aProvider)
private void serializeFeatureStructures(FeatureStructures aAllFSes, JsonGenerator aJg, SerializerProvider aProvider)
throws IOException {
FeatureStructures allFSes = findAllFeatureStructures(aCas);
FeatureStructureToViewIndex.set(aProvider, new FeatureStructureToViewIndex(allFSes));
if (!allFSes.isEmpty()) {
FeatureStructureToViewIndex.set(aProvider, new FeatureStructureToViewIndex(aAllFSes));
if (!aAllFSes.isEmpty()) {
aJg.writeFieldName(FEATURE_STRUCTURES_FIELD);
aProvider.defaultSerializeValue(allFSes, aJg);
aProvider.defaultSerializeValue(aAllFSes, aJg);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import static java.util.stream.Collectors.toList;
import static org.apache.uima.json.jsoncas2.JsonCas2Names.ELEMENT_TYPE_FIELD;
import static org.apache.uima.json.jsoncas2.JsonCas2Names.NAME_FIELD;
import static org.apache.uima.json.jsoncas2.JsonCas2Names.SUPER_TYPE_FIELD;

import java.io.IOException;
Expand All @@ -28,8 +29,6 @@
import org.apache.uima.cas.Feature;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.impl.TypeImpl;
import org.apache.uima.json.jsoncas2.JsonCas2Names;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
Expand All @@ -46,7 +45,7 @@ public void serialize(Type aType, JsonGenerator aJg, SerializerProvider aProvide
throws IOException {
aJg.writeStartObject(aType);

aJg.writeStringField(JsonCas2Names.NAME_FIELD, aType.getName());
aJg.writeStringField(NAME_FIELD, aType.getName());

Type parent = ((TypeImpl) aType).getSuperType();
if (parent != null) {
Expand All @@ -57,13 +56,12 @@ public void serialize(Type aType, JsonGenerator aJg, SerializerProvider aProvide
aJg.writeStringField(ELEMENT_TYPE_FIELD, aType.getComponentType().getName());
}

List<Feature> newFeatures = aType.getFeatures().stream().filter(f -> f.getDomain() == aType)
List<Feature> localFeatures = aType.getFeatures().stream() //
.filter(f -> f.getDomain() == aType) //
.collect(toList());
if (!newFeatures.isEmpty()) {
for (Feature feature : newFeatures) {
aJg.writeFieldName(feature.getShortName());
aProvider.defaultSerializeValue(feature, aJg);
}
for (Feature feature : localFeatures) {
aJg.writeFieldName(feature.getShortName());
aProvider.defaultSerializeValue(feature, aJg);
}

aJg.writeEndObject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,19 @@
import static java.util.stream.Collectors.toList;

import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.StreamSupport;

import org.apache.uima.cas.CAS;
import org.apache.uima.cas.Feature;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.TypeSystem;
import org.apache.uima.json.jsoncas2.mode.TypeSystemMode;
import org.apache.uima.json.jsoncas2.model.FeatureStructures;
import org.apache.uima.json.jsoncas2.ref.ReferenceCache;

import com.fasterxml.jackson.core.JsonGenerator;
Expand All @@ -41,7 +46,7 @@
public class TypeSystemSerializer extends StdSerializer<TypeSystem> {
private static final long serialVersionUID = -4369127219437592227L;

private final Set<String> BUILT_IN_TYPES = unmodifiableSet(new HashSet<>(asList(
private static final Set<String> BUILT_IN_TYPES = unmodifiableSet(new HashSet<>(asList(
CAS.TYPE_NAME_ANNOTATION, CAS.TYPE_NAME_ANNOTATION_BASE, CAS.TYPE_NAME_ARRAY_BASE,
CAS.TYPE_NAME_BOOLEAN, CAS.TYPE_NAME_BOOLEAN_ARRAY, CAS.TYPE_NAME_BYTE,
CAS.TYPE_NAME_BYTE_ARRAY, CAS.TYPE_NAME_DOCUMENT_ANNOTATION, CAS.TYPE_NAME_DOUBLE,
Expand All @@ -65,11 +70,9 @@ public void serialize(TypeSystem aTypeSystem, JsonGenerator jg, SerializerProvid
throws IOException {
ReferenceCache refCache = ReferenceCache.get(aProvider);

jg.writeStartObject(aTypeSystem);
List<Type> types = findTypesToSerialize(aTypeSystem, aProvider);

List<Type> types = StreamSupport.stream(aTypeSystem.spliterator(), false)
.sorted(comparing(Type::getName))
.filter(type -> !BUILT_IN_TYPES.contains(type.getName())).collect(toList());
jg.writeStartObject(aTypeSystem);

for (Type type : types) {
jg.writeFieldName(refCache.typeRef(type));
Expand All @@ -79,4 +82,65 @@ public void serialize(TypeSystem aTypeSystem, JsonGenerator jg, SerializerProvid

jg.writeEndObject();
}

private List<Type> findTypesToSerialize(TypeSystem aTypeSystem, SerializerProvider aProvider) {
Iterable<Type> typesSource = aTypeSystem;

if (TypeSystemMode.get(aProvider) == TypeSystemMode.MINIMAL) {
Deque<Type> queue = collectTypesUsedByFeatureStructures(aTypeSystem, aProvider);
typesSource = collectTypesUsedByTypes(aTypeSystem, typesSource, queue);
}

return StreamSupport.stream(typesSource.spliterator(), false) //
.filter(type -> !type.isArray()) //
.filter(type -> !BUILT_IN_TYPES.contains(type.getName())) //
.sorted(comparing(Type::getName)) //
.collect(toList());
}

private Set<Type> collectTypesUsedByTypes(TypeSystem aTypeSystem, Iterable<Type> typesSource, Deque<Type> queue) {
Set<Type> typeSet = new HashSet<>();
while (!queue.isEmpty()) {
Type t = queue.poll();

if (typeSet.contains(t)) {
continue;
}

for (Feature f : t.getFeatures()) {
Type parent = aTypeSystem.getParent(t);
while (parent != null) {
if (!typeSet.contains(parent)) {
queue.add(parent);
}
parent = aTypeSystem.getParent(parent);
}

Type range = f.getRange();
if (!typeSet.contains(range)) {
queue.add(range);
}

Type componentType = range.getComponentType();
if (componentType != null && !typeSet.contains(typesSource)) {
queue.add(componentType);
}
}

typeSet.add(t);
}
return typeSet;
}

private Deque<Type> collectTypesUsedByFeatureStructures(TypeSystem aTypeSystem,
SerializerProvider aProvider) {
FeatureStructures allFs = FeatureStructures.get(aProvider);
Deque<Type> queue = new ArrayDeque<>();
aTypeSystem.forEach(type -> {
if (allFs.existsAnnotationOfType(type.getName())) {
queue.add(type);
}
});
return queue;
}
}
119 changes: 119 additions & 0 deletions src/test/java/org/apache/uima/json/jsoncas2/ser/CasSerializerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* 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 org.apache.uima.json.jsoncas2.ser;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.uima.UIMAFramework.getResourceSpecifierFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.contentOf;

import java.io.File;

import org.apache.uima.cas.CAS;
import org.apache.uima.cas.FeatureStructure;
import org.apache.uima.json.jsoncas2.JsonCas2Serializer;
import org.apache.uima.json.jsoncas2.mode.TypeSystemMode;
import org.apache.uima.resource.metadata.TypeDescription;
import org.apache.uima.resource.metadata.TypeSystemDescription;
import org.apache.uima.util.CasCreationUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

class CasSerializerTest {
private final static String USED_ANNOTATION_TYPE = "custom.UsedAnnotationType";

private final static String ANNOTATION_TYPE_USED_FROM_RANGE = "custom.AnnotationTypeUsedFromRange";

private final static String ANNOTATION_TYPE_USED_FROM_COMPONENT = "custom.AnnotationTypeUsedFromComponent";

private final static String ANNOTATION_TYPE_USED_FROM_PARENT = "custom.AnnotationTypeUsedFromParent";

private final static String UNUSED_ANNOTATION_TYPE = "custom.UnusedAnnotationType";

private JsonCas2Serializer sut;

@BeforeEach
void setup() {
sut = new JsonCas2Serializer();
}

@Test
void thatUnusedTypesAreNotSerializedInMinimalTypeSystemMode(@TempDir
File aTemp) throws Exception {
CAS cas = CasCreationUtils.createCas(makeTypeSystem(), null, null);
createFeatureStructure(USED_ANNOTATION_TYPE, cas);

File out = new File(aTemp, "out.json");
sut.setTypeSystemMode(TypeSystemMode.MINIMAL);
sut.serialize(cas, out);

assertThat(contentOf(out, UTF_8)).isEqualTo(
contentOf(getClass().getResource("/CasSerializerTest/minimalTypeSystem.json"), UTF_8));
}

@Test
void thatAllTypesAreSerializedInFullTypeSystemMode(@TempDir
File aTemp) throws Exception {
CAS cas = CasCreationUtils.createCas(makeTypeSystem(), null, null);
createFeatureStructure(USED_ANNOTATION_TYPE, cas);

File out = new File(aTemp, "out.json");
sut.setTypeSystemMode(TypeSystemMode.FULL);
sut.serialize(cas, out);

assertThat(contentOf(out, UTF_8)).isEqualTo(
contentOf(getClass().getResource("/CasSerializerTest/fullTypeSystem.json"), UTF_8));
}

@Test
void thatNoTypesAreSerializedInNoTypeSystemMode(@TempDir
File aTemp) throws Exception {
CAS cas = CasCreationUtils.createCas(makeTypeSystem(), null, null);
createFeatureStructure(USED_ANNOTATION_TYPE, cas);

File out = new File(aTemp, "out.json");
sut.setTypeSystemMode(TypeSystemMode.NONE);
sut.serialize(cas, out);

assertThat(contentOf(out, UTF_8)).isEqualTo(
contentOf(getClass().getResource("/CasSerializerTest/noTypeSystem.json"), UTF_8));
}

private TypeSystemDescription makeTypeSystem() {
TypeSystemDescription tsd = getResourceSpecifierFactory().createTypeSystemDescription();
tsd.addType(ANNOTATION_TYPE_USED_FROM_PARENT, null, CAS.TYPE_NAME_ANNOTATION);
tsd.addType(ANNOTATION_TYPE_USED_FROM_RANGE, null, CAS.TYPE_NAME_ANNOTATION);
tsd.addType(ANNOTATION_TYPE_USED_FROM_COMPONENT, null, CAS.TYPE_NAME_ANNOTATION);
tsd.addType(UNUSED_ANNOTATION_TYPE, null, CAS.TYPE_NAME_ANNOTATION);

TypeDescription td = tsd.addType(USED_ANNOTATION_TYPE, null, ANNOTATION_TYPE_USED_FROM_PARENT);
td.addFeature("feat1", null, ANNOTATION_TYPE_USED_FROM_RANGE);
td.addFeature("feat2", null, CAS.TYPE_NAME_FS_ARRAY, ANNOTATION_TYPE_USED_FROM_COMPONENT,
false);
return tsd;
}

private FeatureStructure createFeatureStructure(String usedAnnotationType, CAS cas) {
FeatureStructure fs = cas.createAnnotation(cas.getTypeSystem().getType(usedAnnotationType), 0,
0);
cas.addFsToIndexes(fs);
return fs;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,11 @@
import org.apache.uima.util.TypeSystemUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

public class FeatureDeSerializerTest {
private Logger log = LoggerFactory.getLogger(getClass());

private static final String TYPE = "Type";
private static final String FEATURE = "feature";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,11 @@
import org.apache.uima.resource.metadata.TypeSystemDescription;
import org.apache.uima.util.CasCreationUtils;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

public class TypeDeSerializerTest {
private Logger log = LoggerFactory.getLogger(getClass());

@Test
public void thatTypeDeSerializationWorks() throws Exception {
ObjectMapper mapper = getMapper();
Expand Down
Loading

0 comments on commit 112e561

Please sign in to comment.