diff --git a/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/api/annotation/config/AnnotationAutoConfiguration.java b/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/api/annotation/config/AnnotationAutoConfiguration.java index e6a6edc9c9e..d09856943de 100644 --- a/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/api/annotation/config/AnnotationAutoConfiguration.java +++ b/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/api/annotation/config/AnnotationAutoConfiguration.java @@ -34,6 +34,7 @@ import de.tudarmstadt.ukp.clarin.webanno.api.annotation.rendering.PreRendererImpl; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.rendering.RenderNotificationRenderStep; import de.tudarmstadt.ukp.clarin.webanno.security.UserDao; +import de.tudarmstadt.ukp.inception.annotation.layer.relation.RelationEndpointFeatureSupport; import de.tudarmstadt.ukp.inception.documents.api.RepositoryProperties; import de.tudarmstadt.ukp.inception.preferences.PreferencesService; import de.tudarmstadt.ukp.inception.rendering.coloring.ColoringService; @@ -96,4 +97,10 @@ public UserPreferencesService userPreferencesService( aRepositoryProperties, aColoringService, aAnnotationEditorProperties, aPreferencesService, aUserService); } + + @Bean + public RelationEndpointFeatureSupport relationEndpointFeatureSupport() + { + return new RelationEndpointFeatureSupport(); + } } diff --git a/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/chain/ChainLayerSupport.java b/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/chain/ChainLayerSupport.java index e77bf8c680b..44b925e13d8 100644 --- a/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/chain/ChainLayerSupport.java +++ b/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/chain/ChainLayerSupport.java @@ -188,4 +188,15 @@ public List validateFeatureName(AnnotationFeature aFeature) return Collections.emptyList(); } + + @Override + public boolean isDeletable(AnnotationFeature aFeature) + { + if (Set.of(FEATURE_NAME_FIRST, FEATURE_NAME_NEXT, FEATURE_NAME_REFERENCE, + FEATURE_NAME_REFERENCE_RELATION).contains(aFeature.getName())) { + return false; + } + + return true; + } } diff --git a/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationAdapter.java b/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationAdapter.java index f5bb934132c..33e589c8aaf 100644 --- a/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationAdapter.java +++ b/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationAdapter.java @@ -17,6 +17,8 @@ */ package de.tudarmstadt.ukp.inception.annotation.layer.relation; +import static de.tudarmstadt.ukp.inception.annotation.layer.relation.RelationEndpointFeatureSupport.PREFIX_SOURCE; +import static de.tudarmstadt.ukp.inception.annotation.layer.relation.RelationEndpointFeatureSupport.PREFIX_TARGET; import static de.tudarmstadt.ukp.inception.support.uima.ICasUtil.selectByAddr; import static java.lang.System.currentTimeMillis; import static java.util.Collections.emptyList; @@ -46,6 +48,7 @@ import de.tudarmstadt.ukp.inception.annotation.layer.TypeAdapter_ImplBase; import de.tudarmstadt.ukp.inception.rendering.selection.Selection; import de.tudarmstadt.ukp.inception.rendering.vmodel.VID; +import de.tudarmstadt.ukp.inception.schema.api.AnnotationSchemaService; import de.tudarmstadt.ukp.inception.schema.api.adapter.AnnotationException; import de.tudarmstadt.ukp.inception.schema.api.adapter.FeatureFilter; import de.tudarmstadt.ukp.inception.schema.api.feature.FeatureSupportRegistry; @@ -234,9 +237,9 @@ public List> validate(CAS aCas) @Override public Selection select(VID aVid, AnnotationFS aAnno) { - Selection selection = new Selection(); - AnnotationFS src = getSourceAnnotation(aAnno); - AnnotationFS tgt = getTargetAnnotation(aAnno); + var selection = new Selection(); + var src = getSourceAnnotation(aAnno); + var tgt = getTargetAnnotation(aAnno); if (getLayer().getAttachFeature() != null) { src = FSUtil.getFeature(src, getLayer().getAttachFeature().getName(), @@ -259,10 +262,10 @@ public boolean equivalents(AnnotationFS aFs1, AnnotationFS aFs2, FeatureFilter a // So if the basic span-oriented comparison returned true, we still must ensure that the // relation endpoints are also equivalent. Here, we only consider the endpoint type and // position but not any other features. - AnnotationFS fs1Source = getSourceAnnotation(aFs1); - AnnotationFS fs1Target = getTargetAnnotation(aFs1); - AnnotationFS fs2Source = getSourceAnnotation(aFs2); - AnnotationFS fs2Target = getTargetAnnotation(aFs2); + var fs1Source = getSourceAnnotation(aFs1); + var fs1Target = getTargetAnnotation(aFs1); + var fs2Source = getSourceAnnotation(aFs2); + var fs2Target = getTargetAnnotation(aFs2); return sameBeginEndAndType(fs1Source, fs2Source) && sameBeginEndAndType(fs1Target, fs2Target); @@ -274,4 +277,28 @@ private boolean sameBeginEndAndType(AnnotationFS aFs1, AnnotationFS aFs2) aFs1.getEnd() == aFs2.getEnd() && // Objects.equals(aFs1.getType().getName(), aFs2.getType().getName()); } + + @Override + public void initializeLayerConfiguration(AnnotationSchemaService aSchemaService) + { + var sourceFeature = AnnotationFeature.builder() // + .withLayer(getLayer()) // + .withType(PREFIX_SOURCE + getAttachTypeName()) // + .withName(getSourceFeatureName()) // + .withUiName("Source") // + .withEnabled(true) // + .build(); + + aSchemaService.createFeature(sourceFeature); + + var targetFeature = AnnotationFeature.builder() // + .withLayer(getLayer()) // + .withType(PREFIX_TARGET + getAttachTypeName()) // + .withName(getTargetFeatureName()) // + .withUiName("Target") // + .withEnabled(true) // + .build(); + + aSchemaService.createFeature(targetFeature); + } } diff --git a/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationEndpointFeatureSupport.java b/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationEndpointFeatureSupport.java new file mode 100644 index 00000000000..33acb1b2bf6 --- /dev/null +++ b/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationEndpointFeatureSupport.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Technische Universität Darmstadt under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The Technische Universität Darmstadt + * 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. + * + * 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 de.tudarmstadt.ukp.inception.annotation.layer.relation; + +import static de.tudarmstadt.ukp.inception.annotation.layer.relation.RelationLayerSupport.FEAT_REL_SOURCE; +import static de.tudarmstadt.ukp.inception.annotation.layer.relation.RelationLayerSupport.FEAT_REL_TARGET; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.apache.commons.lang3.StringUtils.substringAfter; + +import java.io.IOException; +import java.io.Serializable; +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.Optional; + +import org.apache.commons.lang3.NotImplementedException; +import org.apache.uima.cas.CAS; +import org.apache.uima.cas.FeatureStructure; +import org.apache.uima.resource.metadata.TypeDescription; +import org.apache.uima.resource.metadata.TypeSystemDescription; +import org.apache.wicket.MarkupContainer; +import org.apache.wicket.markup.html.panel.EmptyPanel; +import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.model.IModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import de.tudarmstadt.ukp.clarin.webanno.api.annotation.config.AnnotationAutoConfiguration; +import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature; +import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer; +import de.tudarmstadt.ukp.inception.editor.action.AnnotationActionHandler; +import de.tudarmstadt.ukp.inception.rendering.editorstate.AnnotatorState; +import de.tudarmstadt.ukp.inception.rendering.editorstate.FeatureState; +import de.tudarmstadt.ukp.inception.schema.api.adapter.AnnotationException; +import de.tudarmstadt.ukp.inception.schema.api.feature.FeatureEditor; +import de.tudarmstadt.ukp.inception.schema.api.feature.FeatureSupport; +import de.tudarmstadt.ukp.inception.schema.api.feature.FeatureType; +import de.tudarmstadt.ukp.inception.support.json.JSONUtil; + +/** + * Extension providing image-related features for annotations. + *

+ * This class is exposed as a Spring Component via + * {@link AnnotationAutoConfiguration#relationEndpointFeatureSupport}. + *

+ */ +public class RelationEndpointFeatureSupport + implements FeatureSupport +{ + private final static Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + public static final String PREFIX_SOURCE = "rel-source:"; + public static final String PREFIX_TARGET = "rel-target:"; + + private String featureSupportId; + + @Autowired + public RelationEndpointFeatureSupport() + { + // Nothing to do + } + + @Override + public String getId() + { + return featureSupportId; + } + + @Override + public void setBeanName(String aBeanName) + { + featureSupportId = aBeanName; + } + + @Override + public Optional getFeatureType(AnnotationFeature aFeature) + { + if (!RelationLayerSupport.TYPE.equals(aFeature.getLayer().getType())) { + return Optional.empty(); + } + + if (FEAT_REL_SOURCE.equals(aFeature.getName())) { + return Optional.of( + new FeatureType(aFeature.getType(), "Relation source", featureSupportId, true)); + } + + if (FEAT_REL_TARGET.equals(aFeature.getName())) { + return Optional.of( + new FeatureType(aFeature.getType(), "Relation target", featureSupportId, true)); + } + + return Optional.empty(); + } + + @Override + public List getSupportedFeatureTypes(AnnotationLayer aAnnotationLayer) + { + if (!RelationLayerSupport.TYPE.equals(aAnnotationLayer.getType())) { + return emptyList(); + } + + var attachType = aAnnotationLayer.getAttachType().getName(); + return asList( // + new FeatureType(PREFIX_SOURCE + attachType, "Relation source", featureSupportId, + true), // + new FeatureType(PREFIX_TARGET + attachType, "Relation target", featureSupportId, + true)); + } + + @Override + public boolean accepts(AnnotationFeature aFeature) + { + return aFeature.getType().startsWith(PREFIX_SOURCE) + || aFeature.getType().startsWith(PREFIX_TARGET); + } + + @Override + public Panel createTraitsEditor(String aId, IModel aFeatureModel) + { + return new EmptyPanel(aId); + } + + @Override + public FeatureEditor createEditor(String aId, MarkupContainer aOwner, + AnnotationActionHandler aHandler, IModel aStateModel, + IModel aFeatureStateModel) + { + return null; + } + + @Override + public RelationEndpointFeatureTraits readTraits(AnnotationFeature aFeature) + { + RelationEndpointFeatureTraits traits = null; + try { + traits = JSONUtil.fromJsonString(RelationEndpointFeatureTraits.class, + aFeature.getTraits()); + } + catch (IOException e) { + LOG.error("Unable to read traits", e); + } + + if (traits == null) { + traits = new RelationEndpointFeatureTraits(); + } + + return traits; + } + + @Override + public void writeTraits(AnnotationFeature aFeature, RelationEndpointFeatureTraits aTraits) + { + try { + aFeature.setTraits(JSONUtil.toJsonString(aTraits)); + } + catch (IOException e) { + LOG.error("Unable to write traits", e); + } + } + + @Override + public void generateFeature(TypeSystemDescription aTSD, TypeDescription aTD, + AnnotationFeature aFeature) + { + if (aFeature.getType().startsWith(PREFIX_SOURCE)) { + aTD.addFeature(aFeature.getName(), "", + substringAfter(aFeature.getType(), PREFIX_SOURCE)); + } + else if (aFeature.getType().startsWith(PREFIX_TARGET)) { + aTD.addFeature(aFeature.getName(), "", + substringAfter(aFeature.getType(), PREFIX_TARGET)); + } + else { + throw new IllegalStateException( + "Unsupported feature type [" + aFeature.getType() + "]"); + } + } + + @Override + public V unwrapFeatureValue(AnnotationFeature aFeature, CAS aCAS, Object aValue) + { + throw new NotImplementedException("Relation endpoints do not support unwrapFeatureValue"); + } + + @Override + public Serializable wrapFeatureValue(AnnotationFeature aFeature, CAS aCAS, Object aValue) + { + throw new NotImplementedException("Relation endpoints do not support wrapFeatureValue"); + } + + @Override + public V getFeatureValue(AnnotationFeature aFeature, FeatureStructure aFS) + { + throw new NotImplementedException("Relation endpoints do not support getFeatureValue"); + } + + @Override + public void setFeatureValue(CAS aCas, AnnotationFeature aFeature, int aAddress, Object aValue) + throws AnnotationException + { + throw new NotImplementedException("Relation endpoints do not support setFeatureValue"); + } + + @Override + public boolean isUsingDefaultOptions(AnnotationFeature aFeature) + { + return false; + } + + @Override + public boolean isAccessible(AnnotationFeature aFeature) + { + return false; + } +} diff --git a/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationEndpointFeatureTraits.java b/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationEndpointFeatureTraits.java new file mode 100644 index 00000000000..51093247b66 --- /dev/null +++ b/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationEndpointFeatureTraits.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Technische Universität Darmstadt under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The Technische Universität Darmstadt + * 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. + * + * 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 de.tudarmstadt.ukp.inception.annotation.layer.relation; + +import java.io.Serializable; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +/** + * Traits for image features. + */ +// The @JsonSerialize annotation avoid the "InvalidDefinitionException: No serializer found" +// exception without having to set SerializationFeature.FAIL_ON_EMPTY_BEANS +@JsonSerialize +public class RelationEndpointFeatureTraits + implements Serializable +{ + private static final long serialVersionUID = -5169695344924699148L; +} diff --git a/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationLayerSupport.java b/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationLayerSupport.java index edcc960de61..05037ec4b71 100644 --- a/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationLayerSupport.java +++ b/inception/inception-api-annotation/src/main/java/de/tudarmstadt/ukp/inception/annotation/layer/relation/RelationLayerSupport.java @@ -24,9 +24,9 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.function.Supplier; -import org.apache.uima.resource.metadata.TypeDescription; import org.apache.uima.resource.metadata.TypeSystemDescription; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; @@ -113,9 +113,8 @@ public boolean accepts(AnnotationLayer aLayer) public RelationAdapter createAdapter(AnnotationLayer aLayer, Supplier> aFeatures) { - RelationAdapter adapter = new RelationAdapter(getLayerSupportRegistry(), - featureSupportRegistry, eventPublisher, aLayer, FEAT_REL_TARGET, FEAT_REL_SOURCE, - aFeatures, + var adapter = new RelationAdapter(getLayerSupportRegistry(), featureSupportRegistry, + eventPublisher, aLayer, FEAT_REL_TARGET, FEAT_REL_SOURCE, aFeatures, layerBehaviorsRegistry.getLayerBehaviors(this, RelationLayerBehavior.class)); return adapter; @@ -125,9 +124,8 @@ public RelationAdapter createAdapter(AnnotationLayer aLayer, public void generateTypes(TypeSystemDescription aTsd, AnnotationLayer aLayer, List aAllFeaturesInProject) { - TypeDescription td = aTsd.addType(aLayer.getName(), aLayer.getDescription(), - TYPE_NAME_ANNOTATION); - AnnotationLayer attachType = aLayer.getAttachType(); + var td = aTsd.addType(aLayer.getName(), aLayer.getDescription(), TYPE_NAME_ANNOTATION); + var attachType = aLayer.getAttachType(); td.addFeature(FEAT_REL_TARGET, "", attachType.getName()); td.addFeature(FEAT_REL_SOURCE, "", attachType.getName()); @@ -149,7 +147,7 @@ public Renderer createRenderer(AnnotationLayer aLayer, @Override public Panel createTraitsEditor(String aId, IModel aLayerModel) { - AnnotationLayer layer = aLayerModel.getObject(); + var layer = aLayerModel.getObject(); if (!accepts(layer)) { throw unsupportedLayerTypeException(layer); @@ -175,4 +173,14 @@ public List validateFeatureName(AnnotationFeature aFeature) return Collections.emptyList(); } + + @Override + public boolean isDeletable(AnnotationFeature aFeature) + { + if (Set.of(FEAT_REL_SOURCE, FEAT_REL_TARGET).contains(aFeature.getName())) { + return false; + } + + return true; + } } diff --git a/inception/inception-diam/src/main/java/de/tudarmstadt/ukp/inception/diam/editor/lazydetails/LazyDetailsLookupServiceImpl.java b/inception/inception-diam/src/main/java/de/tudarmstadt/ukp/inception/diam/editor/lazydetails/LazyDetailsLookupServiceImpl.java index 3005010028a..71b3a9f05dc 100644 --- a/inception/inception-diam/src/main/java/de/tudarmstadt/ukp/inception/diam/editor/lazydetails/LazyDetailsLookupServiceImpl.java +++ b/inception/inception-diam/src/main/java/de/tudarmstadt/ukp/inception/diam/editor/lazydetails/LazyDetailsLookupServiceImpl.java @@ -120,7 +120,7 @@ public List lookupAnnotationLevelDetails(VID aVid, SourceDocum .forEach(detailGroups::add); } else { - for (var feature : annotationService.listAnnotationFeature(aLayer)) { + for (var feature : annotationService.listSupportedFeatures(aLayer)) { lookupFeatureLevelDetails(aVid, aCas, feature).forEach(detailGroups::add); } } @@ -185,6 +185,10 @@ public List lookupFeatureLevelDetails(VID aVid, CAS aCas, var fs = selectFsByAddr(aCas, aVid.getId()); var ext = featureSupportRegistry.findExtension(aFeature).orElseThrow(); + if (!ext.isAccessible(aFeature)) { + return emptyList(); + } + return ext.lookupLazyDetails(aFeature, ext.getFeatureValue(aFeature, fs)); } diff --git a/inception/inception-layer-docmetadata/src/main/java/de/tudarmstadt/ukp/inception/ui/core/docanno/sidebar/DocumentMetadataAnnotationDetailPanel.java b/inception/inception-layer-docmetadata/src/main/java/de/tudarmstadt/ukp/inception/ui/core/docanno/sidebar/DocumentMetadataAnnotationDetailPanel.java index 19c67c42fe1..daf2f0aa4da 100644 --- a/inception/inception-layer-docmetadata/src/main/java/de/tudarmstadt/ukp/inception/ui/core/docanno/sidebar/DocumentMetadataAnnotationDetailPanel.java +++ b/inception/inception-layer-docmetadata/src/main/java/de/tudarmstadt/ukp/inception/ui/core/docanno/sidebar/DocumentMetadataAnnotationDetailPanel.java @@ -256,6 +256,10 @@ private List listFeatures() continue; } + if (!featureSupportRegistry.findExtension(feature).get().isAccessible(feature)) { + continue; + } + Serializable value = null; if (fs != null) { value = adapter.getFeatureValue(feature, fs); diff --git a/inception/inception-processing/src/main/java/de/tudarmstadt/ukp/inception/processing/recommender/BulkRecommenderPanel.java b/inception/inception-processing/src/main/java/de/tudarmstadt/ukp/inception/processing/recommender/BulkRecommenderPanel.java index 34434cffabc..4c3783a08be 100644 --- a/inception/inception-processing/src/main/java/de/tudarmstadt/ukp/inception/processing/recommender/BulkRecommenderPanel.java +++ b/inception/inception-processing/src/main/java/de/tudarmstadt/ukp/inception/processing/recommender/BulkRecommenderPanel.java @@ -53,6 +53,7 @@ import de.tudarmstadt.ukp.inception.rendering.editorstate.FeatureState; import de.tudarmstadt.ukp.inception.scheduling.SchedulingService; import de.tudarmstadt.ukp.inception.schema.api.AnnotationSchemaService; +import de.tudarmstadt.ukp.inception.schema.api.feature.FeatureSupportRegistry; import de.tudarmstadt.ukp.inception.support.lambda.LambdaAjaxButton; import de.tudarmstadt.ukp.inception.support.lambda.LambdaAjaxFormComponentUpdatingBehavior; import de.tudarmstadt.ukp.inception.ui.core.docanno.layer.DocumentMetadataLayerSupport; @@ -67,6 +68,7 @@ public class BulkRecommenderPanel private @SpringBean SchedulingService schedulingService; private @SpringBean UserDao userService; private @SpringBean AnnotationSchemaService annotationSchemaService; + private @SpringBean FeatureSupportRegistry featureSupportRegistry; private CompoundPropertyModel formModel; private FeatureEditorPanel processingMetadata; @@ -178,6 +180,10 @@ private List listFeatureStates() continue; } + if (!featureSupportRegistry.findExtension(feature).get().isAccessible(feature)) { + continue; + } + if (feature.getLinkMode() != LinkMode.NONE) { continue; } diff --git a/inception/inception-project-initializers-basic/src/main/java/de/tudarmstadt/ukp/inception/project/initializers/basic/BasicRelationLayerInitializer.java b/inception/inception-project-initializers-basic/src/main/java/de/tudarmstadt/ukp/inception/project/initializers/basic/BasicRelationLayerInitializer.java index 11dfe489175..617a3e606cf 100644 --- a/inception/inception-project-initializers-basic/src/main/java/de/tudarmstadt/ukp/inception/project/initializers/basic/BasicRelationLayerInitializer.java +++ b/inception/inception-project-initializers-basic/src/main/java/de/tudarmstadt/ukp/inception/project/initializers/basic/BasicRelationLayerInitializer.java @@ -38,7 +38,6 @@ import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature; import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer; import de.tudarmstadt.ukp.clarin.webanno.model.Project; -import de.tudarmstadt.ukp.clarin.webanno.model.TagSet; import de.tudarmstadt.ukp.clarin.webanno.project.initializers.LayerInitializer; import de.tudarmstadt.ukp.inception.project.api.ProjectInitializer; import de.tudarmstadt.ukp.inception.project.initializers.basic.config.InceptionBasicProjectInitializersAutoConfiguration; @@ -111,16 +110,18 @@ public List> getDependencies() @Override public void configure(Project aProject) throws IOException { - AnnotationLayer spanLayer = annotationSchemaService.findLayer(aProject, - BASIC_SPAN_LAYER_NAME); + var spanLayer = annotationSchemaService.findLayer(aProject, BASIC_SPAN_LAYER_NAME); - AnnotationLayer relationLayer = new AnnotationLayer(BASIC_RELATION_LAYER_NAME, "Relation", + var relationLayer = new AnnotationLayer(BASIC_RELATION_LAYER_NAME, "Relation", RELATION_TYPE, aProject, false, TOKENS, OVERLAP_ONLY); relationLayer.setCrossSentence(false); relationLayer.setAttachType(spanLayer); annotationSchemaService.createOrUpdateLayer(relationLayer); - TagSet relationTagSet = annotationSchemaService.getTagSet(BASIC_RELATION_TAG_SET_NAME, + annotationSchemaService.getAdapter(relationLayer) + .initializeLayerConfiguration(annotationSchemaService); + + var relationTagSet = annotationSchemaService.getTagSet(BASIC_RELATION_TAG_SET_NAME, aProject); annotationSchemaService.createFeature( diff --git a/inception/inception-project-initializers/src/main/java/de/tudarmstadt/ukp/clarin/webanno/project/initializers/CoreferenceLayerInitializer.java b/inception/inception-project-initializers/src/main/java/de/tudarmstadt/ukp/clarin/webanno/project/initializers/CoreferenceLayerInitializer.java index 20ae36f5509..e2be01c0f6f 100644 --- a/inception/inception-project-initializers/src/main/java/de/tudarmstadt/ukp/clarin/webanno/project/initializers/CoreferenceLayerInitializer.java +++ b/inception/inception-project-initializers/src/main/java/de/tudarmstadt/ukp/clarin/webanno/project/initializers/CoreferenceLayerInitializer.java @@ -35,7 +35,6 @@ import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer; import de.tudarmstadt.ukp.clarin.webanno.model.OverlapMode; import de.tudarmstadt.ukp.clarin.webanno.model.Project; -import de.tudarmstadt.ukp.clarin.webanno.model.TagSet; import de.tudarmstadt.ukp.clarin.webanno.project.initializers.config.ProjectInitializersAutoConfiguration; import de.tudarmstadt.ukp.inception.project.api.ProjectInitializer; import de.tudarmstadt.ukp.inception.schema.api.AnnotationSchemaService; @@ -100,16 +99,20 @@ public boolean alreadyApplied(Project aProject) @Override public void configure(Project aProject) throws IOException { - TagSet corefTypeTagSet = annotationSchemaService + var corefTypeTagSet = annotationSchemaService .getTagSet(CoreferenceTypeTagSetInitializer.TAG_SET_NAME, aProject); - TagSet corefRelTagSet = annotationSchemaService + var corefRelTagSet = annotationSchemaService .getTagSet(CoreferenceRelationTagSetInitializer.TAG_SET_NAME, aProject); - AnnotationLayer base = new AnnotationLayer(COREFERENCE_LAYER_NAME, "Coreference", - CHAIN_TYPE, aProject, true, AnchoringMode.TOKENS, OverlapMode.ANY_OVERLAP); + var base = new AnnotationLayer(COREFERENCE_LAYER_NAME, "Coreference", CHAIN_TYPE, aProject, + true, AnchoringMode.TOKENS, OverlapMode.ANY_OVERLAP); base.setCrossSentence(true); annotationSchemaService.createOrUpdateLayer(base); + // FIXME: should probably be replaced by calling + // annotationSchemaService.getAdapter(base) + // .initializeLayerConfiguration(annotationSchemaService); + annotationSchemaService.createFeature(new AnnotationFeature(aProject, base, "referenceType", "referenceType", CAS.TYPE_NAME_STRING, "Coreference type", corefTypeTagSet)); annotationSchemaService.createFeature( diff --git a/inception/inception-project-initializers/src/main/java/de/tudarmstadt/ukp/clarin/webanno/project/initializers/DependencyLayerInitializer.java b/inception/inception-project-initializers/src/main/java/de/tudarmstadt/ukp/clarin/webanno/project/initializers/DependencyLayerInitializer.java index 373462531e0..2fe43d4bc1d 100644 --- a/inception/inception-project-initializers/src/main/java/de/tudarmstadt/ukp/clarin/webanno/project/initializers/DependencyLayerInitializer.java +++ b/inception/inception-project-initializers/src/main/java/de/tudarmstadt/ukp/clarin/webanno/project/initializers/DependencyLayerInitializer.java @@ -35,7 +35,6 @@ import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature; import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer; import de.tudarmstadt.ukp.clarin.webanno.model.Project; -import de.tudarmstadt.ukp.clarin.webanno.model.TagSet; import de.tudarmstadt.ukp.clarin.webanno.project.initializers.config.ProjectInitializersAutoConfiguration; import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Token; import de.tudarmstadt.ukp.dkpro.core.api.syntax.type.dependency.Dependency; @@ -103,14 +102,12 @@ public boolean alreadyApplied(Project aProject) public void configure(Project aProject) throws IOException { // Dependency Layer - AnnotationLayer depLayer = new AnnotationLayer(Dependency.class.getName(), "Dependency", - RELATION_TYPE, aProject, true, SINGLE_TOKEN, OVERLAP_ONLY); - AnnotationLayer tokenLayer = annotationSchemaService.findLayer(aProject, - Token.class.getName()); - List tokenFeatures = annotationSchemaService - .listAnnotationFeature(tokenLayer); + var depLayer = new AnnotationLayer(Dependency.class.getName(), "Dependency", RELATION_TYPE, + aProject, true, SINGLE_TOKEN, OVERLAP_ONLY); + var tokenLayer = annotationSchemaService.findLayer(aProject, Token.class.getName()); + var tokenFeatures = annotationSchemaService.listAnnotationFeature(tokenLayer); AnnotationFeature tokenPosFeature = null; - for (AnnotationFeature feature : tokenFeatures) { + for (var feature : tokenFeatures) { if (feature.getName().equals("pos")) { tokenPosFeature = feature; break; @@ -118,16 +115,19 @@ public void configure(Project aProject) throws IOException } depLayer.setAttachType(tokenLayer); depLayer.setAttachFeature(tokenPosFeature); + annotationSchemaService.createOrUpdateLayer(depLayer); + + annotationSchemaService.getAdapter(depLayer) + .initializeLayerConfiguration(annotationSchemaService); - TagSet depTagSet = annotationSchemaService + var depTagSet = annotationSchemaService .getTagSet(DependencyTypeTagSetInitializer.TAG_SET_NAME, aProject); - annotationSchemaService.createOrUpdateLayer(depLayer); annotationSchemaService .createFeature(new AnnotationFeature(aProject, depLayer, "DependencyType", "Relation", CAS.TYPE_NAME_STRING, "Dependency relation", depTagSet)); - TagSet flavorsTagset = annotationSchemaService + var flavorsTagset = annotationSchemaService .getTagSet(DependencyFlavorTagSetInitializer.TAG_SET_NAME, aProject); annotationSchemaService.createFeature(new AnnotationFeature(aProject, depLayer, "flavor", diff --git a/inception/inception-review-editor/src/main/java/de/tudarmstadt/ukp/inception/revieweditor/DocumentAnnotationPanel.java b/inception/inception-review-editor/src/main/java/de/tudarmstadt/ukp/inception/revieweditor/DocumentAnnotationPanel.java index ab826028de9..c554a958641 100644 --- a/inception/inception-review-editor/src/main/java/de/tudarmstadt/ukp/inception/revieweditor/DocumentAnnotationPanel.java +++ b/inception/inception-review-editor/src/main/java/de/tudarmstadt/ukp/inception/revieweditor/DocumentAnnotationPanel.java @@ -151,11 +151,15 @@ private List listFeatures() // Populate from feature structure List featureStates = new ArrayList<>(); - for (AnnotationFeature feature : annotationService.listAnnotationFeature(layer)) { + for (AnnotationFeature feature : annotationService.listSupportedFeatures(layer)) { if (!feature.isEnabled()) { continue; } + if (!featureSupportRegistry.findExtension(feature).get().isAccessible(feature)) { + continue; + } + Serializable value = null; if (fs != null) { value = adapter.getFeatureValue(feature, fs); diff --git a/inception/inception-review-editor/src/main/java/de/tudarmstadt/ukp/inception/revieweditor/SpanAnnotationPanel.java b/inception/inception-review-editor/src/main/java/de/tudarmstadt/ukp/inception/revieweditor/SpanAnnotationPanel.java index e7a5c6d47b9..995b28c9752 100644 --- a/inception/inception-review-editor/src/main/java/de/tudarmstadt/ukp/inception/revieweditor/SpanAnnotationPanel.java +++ b/inception/inception-review-editor/src/main/java/de/tudarmstadt/ukp/inception/revieweditor/SpanAnnotationPanel.java @@ -140,11 +140,15 @@ private List listFeatures(FeatureStructure aFs, AnnotationLayer aL // Populate from feature structure List featureStates = new ArrayList<>(); - for (AnnotationFeature feature : annotationService.listAnnotationFeature(aLayer)) { + for (AnnotationFeature feature : annotationService.listSupportedFeatures(aLayer)) { if (!feature.isEnabled()) { continue; } + if (!featureSupportRegistry.findExtension(feature).get().isAccessible(feature)) { + continue; + } + Serializable value = null; if (aFs != null) { value = adapter.getFeatureValue(feature, aFs); diff --git a/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/rendering/Renderer.java b/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/rendering/Renderer.java index 811b633c997..bce7e2fba63 100644 --- a/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/rendering/Renderer.java +++ b/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/rendering/Renderer.java @@ -25,7 +25,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import org.apache.commons.lang3.StringUtils; import org.apache.uima.cas.CAS; @@ -84,10 +83,16 @@ default Map renderLabelFeatureValues(TypeAdapter aAdapter, Featu continue; } - var label = defaultString(fsr.findExtension(feature) - .orElseThrow(() -> new NoSuchElementException( - "No feature support found for feature " + feature)) - .renderFeatureValue(feature, aFs)); + var maybeFeatureSupport = fsr.findExtension(feature); + if (maybeFeatureSupport.isEmpty()) { + continue; + } + + if (!maybeFeatureSupport.get().isAccessible(feature)) { + continue; + } + + var label = defaultString(maybeFeatureSupport.get().renderFeatureValue(feature, aFs)); features.put(feature.getName(), label); } diff --git a/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/schema/api/feature/FeatureSupport.java b/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/schema/api/feature/FeatureSupport.java index 1c8106957da..634b1d99a64 100644 --- a/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/schema/api/feature/FeatureSupport.java +++ b/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/schema/api/feature/FeatureSupport.java @@ -400,8 +400,8 @@ default boolean suppressAutoFocus(AnnotationFeature aFeature) default FeatureStructure getFS(CAS aCas, AnnotationFeature aFeature, int aAddress) { - FeatureStructure fs = selectFsByAddr(aCas, aAddress); - Feature feature = fs.getType().getFeatureByBaseName(aFeature.getName()); + var fs = selectFsByAddr(aCas, aAddress); + var feature = fs.getType().getFeatureByBaseName(aFeature.getName()); if (feature == null) { throw new IllegalArgumentException("On [" + fs.getType().getName() + "] the feature [" @@ -409,4 +409,21 @@ default FeatureStructure getFS(CAS aCas, AnnotationFeature aFeature, int aAddres } return fs; } + + default boolean isUsingDefaultOptions(AnnotationFeature aFeature) + { + return true; + } + + /** + * @return Whether the given feature is accessible. Non-accessible feature do not need to + * support {@link #getFeatureValue}, {@link #setFeatureValue}, {@link #wrapFeatureValue} + * and {@link #unwrapFeatureValue}. + * @param aFeature + * the feature + */ + default boolean isAccessible(AnnotationFeature aFeature) + { + return true; + } } diff --git a/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/schema/api/layer/LayerSupport.java b/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/schema/api/layer/LayerSupport.java index bba1cd763e2..14897b79237 100644 --- a/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/schema/api/layer/LayerSupport.java +++ b/inception/inception-schema-api/src/main/java/de/tudarmstadt/ukp/inception/schema/api/layer/LayerSupport.java @@ -181,4 +181,9 @@ default List validateFeatureName(AnnotationFeature aFeature) { return Collections.emptyList(); } + + default boolean isDeletable(AnnotationFeature aFeature) + { + return true; + } } diff --git a/inception/inception-schema/src/main/java/de/tudarmstadt/ukp/inception/schema/service/AnnotationSchemaServiceImpl.java b/inception/inception-schema/src/main/java/de/tudarmstadt/ukp/inception/schema/service/AnnotationSchemaServiceImpl.java index 840cc7f8c65..3279ed2f9fb 100644 --- a/inception/inception-schema/src/main/java/de/tudarmstadt/ukp/inception/schema/service/AnnotationSchemaServiceImpl.java +++ b/inception/inception-schema/src/main/java/de/tudarmstadt/ukp/inception/schema/service/AnnotationSchemaServiceImpl.java @@ -670,7 +670,9 @@ public AnnotationFeature getFeature(String aName, AnnotationLayer aLayer) return entityManager .createQuery("From AnnotationFeature where name = :name AND layer = :layer", AnnotationFeature.class) - .setParameter("name", aName).setParameter("layer", aLayer).getSingleResult(); + .setParameter("name", aName) // + .setParameter("layer", aLayer) // + .getSingleResult(); } @Override @@ -748,7 +750,9 @@ public List listAnnotationLayer(Project aProject) @Transactional public List listAttachedRelationLayers(AnnotationLayer aLayer) { - String query = String.join("\n", + Objects.requireNonNull(aLayer, "Parameter [layer] must be specified"); + + var query = String.join("\n", "SELECT l FROM AnnotationLayer l LEFT JOIN l.attachFeature f ", // "WHERE l.type = :type AND ", // " l.project = :project AND ", // diff --git a/inception/inception-ui-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/annotation/detail/AnnotationDetailEditorPanel.java b/inception/inception-ui-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/annotation/detail/AnnotationDetailEditorPanel.java index 30d9c5313a7..019f55699b3 100644 --- a/inception/inception-ui-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/annotation/detail/AnnotationDetailEditorPanel.java +++ b/inception/inception-ui-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/annotation/detail/AnnotationDetailEditorPanel.java @@ -104,6 +104,7 @@ import de.tudarmstadt.ukp.inception.schema.api.adapter.AnnotationException; import de.tudarmstadt.ukp.inception.schema.api.adapter.TypeAdapter; import de.tudarmstadt.ukp.inception.schema.api.config.AnnotationSchemaProperties; +import de.tudarmstadt.ukp.inception.schema.api.feature.FeatureSupportRegistry; import de.tudarmstadt.ukp.inception.schema.api.feature.LinkWithRoleModel; import de.tudarmstadt.ukp.inception.support.lambda.LambdaAjaxLink; import de.tudarmstadt.ukp.inception.support.lambda.LambdaBehavior; @@ -125,6 +126,7 @@ public abstract class AnnotationDetailEditorPanel private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private @SpringBean AnnotationSchemaService annotationService; + private @SpringBean FeatureSupportRegistry featureSupportRegistry; private @SpringBean AnnotationSchemaProperties annotationEditorProperties; // Top-level containers @@ -1116,10 +1118,14 @@ private void loadFeatureEditorModelsCommon(AjaxRequestTarget aTarget, CAS aCas, { getModelObject().getFeatureStates().clear(); - AnnotatorState state = AnnotationDetailEditorPanel.this.getModelObject(); + var state = AnnotationDetailEditorPanel.this.getModelObject(); // Populate from feature structure - for (AnnotationFeature feature : annotationService.listSupportedFeatures(aLayer)) { + for (var feature : annotationService.listSupportedFeatures(aLayer)) { + if (!featureSupportRegistry.findExtension(feature).get().isAccessible(feature)) { + continue; + } + if (!feature.isEnabled()) { continue; } diff --git a/inception/inception-ui-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/annotation/detail/AnnotationTextPanel.html b/inception/inception-ui-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/annotation/detail/AnnotationTextPanel.html index 4ebf9c2fb32..5d3b61f655f 100644 --- a/inception/inception-ui-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/annotation/detail/AnnotationTextPanel.html +++ b/inception/inception-ui-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/annotation/detail/AnnotationTextPanel.html @@ -34,7 +34,7 @@
@@ -47,7 +47,7 @@
diff --git a/inception/inception-ui-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/annotation/detail/AnnotationTextPanel.java b/inception/inception-ui-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/annotation/detail/AnnotationTextPanel.java index cce556ae5ce..c424704b636 100644 --- a/inception/inception-ui-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/annotation/detail/AnnotationTextPanel.java +++ b/inception/inception-ui-annotation/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/annotation/detail/AnnotationTextPanel.java @@ -17,14 +17,19 @@ */ package de.tudarmstadt.ukp.clarin.webanno.ui.annotation.detail; +import static de.tudarmstadt.ukp.inception.annotation.layer.relation.RelationLayerSupport.FEAT_REL_SOURCE; +import static de.tudarmstadt.ukp.inception.annotation.layer.relation.RelationLayerSupport.FEAT_REL_TARGET; import static de.tudarmstadt.ukp.inception.support.lambda.LambdaBehavior.visibleWhen; import java.io.IOException; +import jakarta.persistence.NoResultException; + import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; +import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.spring.injection.annot.SpringBean; @@ -58,8 +63,12 @@ public AnnotationTextPanel(String aId, AnnotationActionHandler aActionHandler, .setAlwaysEnabled(true) // avoid disabling in read-only mode .add(visibleWhen(() -> !isRelationSelected()))); + add(new Label("originName", LoadableDetachableModel.of(this::getOriginName)) + .add(visibleWhen(() -> isRelationSelected()))); add(new Label("originText", PropertyModel.of(getModelObject(), "selection.originText")) .add(visibleWhen(() -> isRelationSelected()))); + add(new Label("targetName", LoadableDetachableModel.of(this::getTargetName)) + .add(visibleWhen(() -> isRelationSelected()))); add(new Label("targetText", PropertyModel.of(getModelObject(), "selection.targetText")) .add(visibleWhen(() -> isRelationSelected()))); add(new LambdaAjaxLink("jumpToOrigin", this::actionJumpToOrigin) // @@ -70,6 +79,30 @@ public AnnotationTextPanel(String aId, AnnotationActionHandler aActionHandler, .add(visibleWhen(() -> isRelationSelected()))); } + private String getOriginName() + { + try { + return annotationService + .getFeature(FEAT_REL_SOURCE, getModelObject().getSelectedAnnotationLayer()) + .getUiName(); + } + catch (NoResultException e) { + return "From"; + } + } + + private String getTargetName() + { + try { + return annotationService + .getFeature(FEAT_REL_TARGET, getModelObject().getSelectedAnnotationLayer()) + .getUiName(); + } + catch (NoResultException e) { + return "To"; + } + } + private boolean isRelationSelected() { return getModelObject().getSelection().isArc(); diff --git a/inception/inception-ui-project/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/project/layers/FeatureDetailForm.java b/inception/inception-ui-project/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/project/layers/FeatureDetailForm.java index 9613cbcf244..45e1da37bf3 100644 --- a/inception/inception-ui-project/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/project/layers/FeatureDetailForm.java +++ b/inception/inception-ui-project/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/project/layers/FeatureDetailForm.java @@ -60,6 +60,7 @@ import de.tudarmstadt.ukp.inception.schema.api.feature.FeatureSupport; import de.tudarmstadt.ukp.inception.schema.api.feature.FeatureSupportRegistry; import de.tudarmstadt.ukp.inception.schema.api.feature.FeatureType; +import de.tudarmstadt.ukp.inception.schema.api.layer.LayerSupportRegistry; import de.tudarmstadt.ukp.inception.support.lambda.LambdaAjaxButton; import de.tudarmstadt.ukp.inception.support.lambda.LambdaBehavior; import de.tudarmstadt.ukp.inception.support.lambda.LambdaButton; @@ -76,6 +77,7 @@ public class FeatureDetailForm private static final long serialVersionUID = -1L; + private @SpringBean LayerSupportRegistry layerSupportRegistry; private @SpringBean FeatureSupportRegistry featureSupportRegistry; private @SpringBean AnnotationSchemaService annotationService; private @SpringBean DocumentService documentService; @@ -85,6 +87,7 @@ public class FeatureDetailForm private final DropDownChoice featureType; private final CheckBox required; private final WebMarkupContainer traitsContainer; + private WebMarkupContainer defaultOptionsContainer; private final ChallengeResponseDialog confirmationDialog; private final TextField uiName; @@ -103,18 +106,13 @@ public FeatureDetailForm(String id, IModel aFeature) uiName.setOutputMarkupId(true); add(uiName); add(new TextArea("description")); - add(new CheckBox("enabled").setOutputMarkupPlaceholderTag(true)); - add(new CheckBox("visible").setOutputMarkupPlaceholderTag(true)); - add(new CheckBox("curatable").setOutputMarkupPlaceholderTag(true)); - add(new CheckBox("hideUnconstraintFeature").setOutputMarkupPlaceholderTag(true)); - add(new CheckBox("remember").setOutputMarkupPlaceholderTag(true)); - add(new CheckBox("includeInHover").setOutputMarkupPlaceholderTag(true) - .add(LambdaBehavior.visibleWhen(() -> { - String layertype = FeatureDetailForm.this.getModelObject().getLayer().getType(); - // Currently not configurable for chains or relations - // TODO: technically it is possible - return !CHAIN_TYPE.equals(layertype) && !RELATION_TYPE.equals(layertype); - }))); + + defaultOptionsContainer = new WebMarkupContainer("defaultOptionsContainer"); + defaultOptionsContainer.add(LambdaBehavior.visibleWhen(this::isUsingDefaultOptions)); + add(defaultOptionsContainer); + defaultOptionsContainer.add(new CheckBox("enabled").setOutputMarkupPlaceholderTag(true)); + defaultOptionsContainer.add(new CheckBox("curatable").setOutputMarkupPlaceholderTag(true)); + defaultOptionsContainer.add(new CheckBox("remember").setOutputMarkupPlaceholderTag(true)); required = new CheckBox("required"); required.setOutputMarkupPlaceholderTag(true); required.add(LambdaBehavior.onConfigure(_this -> { @@ -129,7 +127,18 @@ public FeatureDetailForm(String id, IModel aFeature) required.setModel(PropertyModel.of(FeatureDetailForm.this.getModel(), "required")); } })); - add(required); + defaultOptionsContainer.add(required); + + defaultOptionsContainer.add(new CheckBox("visible").setOutputMarkupPlaceholderTag(true)); + defaultOptionsContainer + .add(new CheckBox("hideUnconstraintFeature").setOutputMarkupPlaceholderTag(true)); + defaultOptionsContainer.add(new CheckBox("includeInHover") + .setOutputMarkupPlaceholderTag(true).add(LambdaBehavior.visibleWhen(() -> { + var layertype = FeatureDetailForm.this.getModelObject().getLayer().getType(); + // Currently not configurable for chains or relations + // TODO: technically it is possible + return !CHAIN_TYPE.equals(layertype) && !RELATION_TYPE.equals(layertype); + }))); add(featureType = new DropDownChoice("type") { @@ -188,8 +197,7 @@ protected void onUpdate(AjaxRequestTarget aTarget) // we clear the currently selected feature. add(new LambdaAjaxButton<>("save", this::actionSave).triggerAfterSubmit()); add(new LambdaAjaxButton<>("delete", this::actionDelete) - .add(enabledWhen(() -> !isNull(getModelObject().getId()) - && !getModelObject().getLayer().isBuiltIn()))); + .add(enabledWhen(() -> isDeletable()))); // Set default form processing to false to avoid saving data add(new LambdaButton("cancel", this::actionCancel).setDefaultFormProcessing(false)); @@ -198,6 +206,32 @@ protected void onUpdate(AjaxRequestTarget aTarget) add(confirmationDialog); } + private boolean isUsingDefaultOptions() + { + var feature = getModelObject(); + if (isNull(feature.getId())) { + return false; + } + + return featureSupportRegistry.findExtension(feature) // + .map(ext -> ext.isUsingDefaultOptions(feature)) // + .orElse(false); + } + + private boolean isDeletable() + { + var feature = getModelObject(); + if (isNull(feature.getId())) { + return false; + } + + if (feature.getLayer().isBuiltIn()) { + return false; + } + + return layerSupportRegistry.getLayerSupport(feature.getLayer()).isDeletable(feature); + } + public Component getInitialFocusComponent() { return uiName; diff --git a/inception/inception-ui-project/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/project/layers/LayerDetailForm.java b/inception/inception-ui-project/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/project/layers/LayerDetailForm.java index 8a66cfbf1ce..b37661ab4d0 100644 --- a/inception/inception-ui-project/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/project/layers/LayerDetailForm.java +++ b/inception/inception-ui-project/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/project/layers/LayerDetailForm.java @@ -65,7 +65,6 @@ import de.tudarmstadt.ukp.inception.documents.api.DocumentService; import de.tudarmstadt.ukp.inception.export.LayerImportExportUtils; import de.tudarmstadt.ukp.inception.schema.api.AnnotationSchemaService; -import de.tudarmstadt.ukp.inception.schema.api.adapter.TypeAdapter; import de.tudarmstadt.ukp.inception.schema.api.event.LayerConfigurationChangedEvent; import de.tudarmstadt.ukp.inception.schema.api.layer.LayerSupport; import de.tudarmstadt.ukp.inception.schema.api.layer.LayerSupportRegistry; @@ -325,16 +324,16 @@ private void actionSave(AjaxRequestTarget aTarget, Form aForm) aTarget.add(getParent()); aTarget.addChildren(getPage(), IFeedback.class); - AnnotationLayer layer = aForm.getModelObject(); + var layer = aForm.getModelObject(); - final Project project = layer.getProject(); + final var project = layer.getProject(); // Set type name only when the layer is initially created. After that, only the UI // name may be updated. Also any validation related to the type name only needs to // happen on the initial creation. - boolean isNewLayer = isNull(layer.getId()); + var isNewLayer = isNull(layer.getId()); if (isNewLayer) { - String layerName = StringUtils.capitalize(layer.getUiName()); + var layerName = StringUtils.capitalize(layer.getUiName()); layerName = layerName.replaceAll("\\W", ""); if (layerName.isEmpty()) { @@ -370,7 +369,7 @@ private void actionSave(AjaxRequestTarget aTarget, Form aForm) // Initialize default features if necessary but only after the layer has actually been // persisted in the database. if (isNewLayer) { - TypeAdapter adapter = annotationService.getAdapter(layer); + var adapter = annotationService.getAdapter(layer); adapter.initializeLayerConfiguration(annotationService); } diff --git a/inception/inception-ui-project/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/project/layers/ProjectLayersPanel.html b/inception/inception-ui-project/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/project/layers/ProjectLayersPanel.html index dce95eb88ef..7249f51c866 100644 --- a/inception/inception-ui-project/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/project/layers/ProjectLayersPanel.html +++ b/inception/inception-ui-project/src/main/java/de/tudarmstadt/ukp/clarin/webanno/ui/project/layers/ProjectLayersPanel.html @@ -306,59 +306,61 @@
-
- -
-
- - -
-
- - -
-
- - -
-
- - +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
-
-
- -
-
- - -
-
- - -
-
- - +
+ +
+
+ + +
+
+ + +
+
+ + +
diff --git a/inception/pom.xml b/inception/pom.xml index a170b68a5bf..b741b7f8fd9 100644 --- a/inception/pom.xml +++ b/inception/pom.xml @@ -851,7 +851,7 @@ com.puppycrawl.tools checkstyle - 10.12.3 + 10.14.2 diff --git a/pom.xml b/pom.xml index e1ddf9da85c..9a8e1655188 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ 1.10.14 4.0.20 - + 5.10.2 1.10.2 5.11.0 @@ -144,9 +144,9 @@ 5.5.0 2.2.3 - 2.5.11 - 2.2.14 - 2.3.10 + 2.5.12 + 2.3.0 + 2.3.15 20230227 2.6.0