Skip to content

Commit

Permalink
Merge branch 'main' into refactoring/Upgrade-to-Spring-Boot-3
Browse files Browse the repository at this point in the history
* main:
  #4545 - Upgrade dependencies
  #4641 - Ability to assign names to endpoints of relations

% Conflicts:
%	pom.xml
  • Loading branch information
reckart committed Mar 18, 2024
2 parents b7e9a68 + abaf6d6 commit 33a66cd
Show file tree
Hide file tree
Showing 26 changed files with 579 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -96,4 +97,10 @@ public UserPreferencesService userPreferencesService(
aRepositoryProperties, aColoringService, aAnnotationEditorProperties,
aPreferencesService, aUserService);
}

@Bean
public RelationEndpointFeatureSupport relationEndpointFeatureSupport()
{
return new RelationEndpointFeatureSupport();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,15 @@ public List<ValidationError> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -234,9 +237,9 @@ public List<Pair<LogMessage, AnnotationFS>> 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(),
Expand All @@ -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);
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* This class is exposed as a Spring Component via
* {@link AnnotationAutoConfiguration#relationEndpointFeatureSupport}.
* </p>
*/
public class RelationEndpointFeatureSupport
implements FeatureSupport<RelationEndpointFeatureTraits>
{
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<FeatureType> 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<FeatureType> 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<AnnotationFeature> aFeatureModel)
{
return new EmptyPanel(aId);
}

@Override
public FeatureEditor createEditor(String aId, MarkupContainer aOwner,
AnnotationActionHandler aHandler, IModel<AnnotatorState> aStateModel,
IModel<FeatureState> 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> 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> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit 33a66cd

Please sign in to comment.