<commit_before>/*
 * Copyright 2019
 * Ubiquitous Knowledge Processing (UKP) Lab and FG Language Technology
 * Technische Universität Darmstadt
 *
 * Licensed 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 de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature;

import static java.util.Arrays.asList;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

import org.apache.uima.cas.CAS;
import org.apache.wicket.MarkupContainer;
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 org.springframework.stereotype.Component;

import de.tudarmstadt.ukp.clarin.webanno.api.AnnotationSchemaService;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.action.AnnotationActionHandler;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.config.PrimitiveUimaFeatureSupportProperties;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.FeatureEditor;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.InputFieldTextFeatureEditor;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.KendoAutoCompleteTextFeatureEditor;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.KendoComboboxTextFeatureEditor;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.StringFeatureTraits;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.StringFeatureTraitsEditor;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.TextAreaFeatureEditor;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.model.AnnotatorState;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.model.FeatureState;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer;
import de.tudarmstadt.ukp.clarin.webanno.model.MultiValueMode;
import de.tudarmstadt.ukp.clarin.webanno.model.Tag;
import de.tudarmstadt.ukp.clarin.webanno.support.JSONUtil;

@Component
public class StringFeatureSupport
    extends UimaPrimitiveFeatureSupport_ImplBase<StringFeatureTraits>
{
    private final Logger log = LoggerFactory.getLogger(getClass());
    
    private List<FeatureType> primitiveTypes;

    private final PrimitiveUimaFeatureSupportProperties properties;
    
    private final AnnotationSchemaService schemaService;

    /*
     * Constructor for use in unit tests to avoid having to always instantiate the properties.
     */
    public StringFeatureSupport()
    {
        this(new PrimitiveUimaFeatureSupportProperties(), null);
    }

    @Autowired
    public StringFeatureSupport(PrimitiveUimaFeatureSupportProperties aProperties,
            @Autowired(required = false) AnnotationSchemaService aSchemaService)
    {
        properties = aProperties;
        schemaService = aSchemaService;
    }

    @Override
    public void afterPropertiesSet() throws Exception
    {
        primitiveTypes = asList(
                new FeatureType(CAS.TYPE_NAME_STRING, "Primitive: String", getId()));
    }
    
    @Override
    public List<FeatureType> getSupportedFeatureTypes(AnnotationLayer aAnnotationLayer)
    {
        return Collections.unmodifiableList(primitiveTypes);
    }

    @Override
    public boolean accepts(AnnotationFeature aFeature)
    {
        return MultiValueMode.NONE.equals(aFeature.getMultiValueMode())
                && CAS.TYPE_NAME_STRING.equals(aFeature.getType());
    }

    @Override
    public void setFeatureValue(CAS aCas, AnnotationFeature aFeature, int aAddress, Object aValue)
    {
        if (
                aValue != null &&
                schemaService != null && 
                aFeature.getTagset() != null && 
                CAS.TYPE_NAME_STRING.equals(aFeature.getType()) && 
                !schemaService.existsTag((String) aValue, aFeature.getTagset())
        ) {
            if (!aFeature.getTagset().isCreateTag()) {
                throw new IllegalArgumentException("[" + aValue
                        + "] is not in the tag list. Please choose from the existing tags");
            }
            else {
                Tag selectedTag = new Tag();
                selectedTag.setName((String) aValue);
                selectedTag.setTagSet(aFeature.getTagset());
                schemaService.createTag(selectedTag);
            }
        }
        
        super.setFeatureValue(aCas, aFeature, aAddress, aValue);
    }
    
    @Override
    public Panel createTraitsEditor(String aId,  IModel<AnnotationFeature> aFeatureModel)
    {
        AnnotationFeature feature = aFeatureModel.getObject();
        
        if (!accepts(feature)) {
            throw unsupportedFeatureTypeException(feature);
        }
        
        return new StringFeatureTraitsEditor(aId, this, aFeatureModel);
    }
    
    @Override
    public FeatureEditor createEditor(String aId, MarkupContainer aOwner,
            AnnotationActionHandler aHandler, final IModel<AnnotatorState> aStateModel,
            final IModel<FeatureState> aFeatureStateModel)
    {
        AnnotationFeature feature = aFeatureStateModel.getObject().feature;

        if (!accepts(feature)) {
            throw unsupportedFeatureTypeException(feature);
        }

        StringFeatureTraits traits = readTraits(feature);
        if (feature.getTagset() == null) {
            if (traits.isMultipleRows()) {
                // If multiple rows are set use a textarea
                return new TextAreaFeatureEditor(aId, aOwner, aFeatureStateModel);
            }
            else {
                // Otherwise use a simple input field
                return new InputFieldTextFeatureEditor(aId, aOwner, aFeatureStateModel, aHandler);
            }
        }

        if (aFeatureStateModel.getObject().tagset.size() < properties.getAutoCompleteThreshold()) {
            // For smaller tagsets, use a combobox
            return new KendoComboboxTextFeatureEditor(aId, aOwner, aFeatureStateModel, aHandler);
        }

        // For larger ones, use an auto-complete field
        return new KendoAutoCompleteTextFeatureEditor(aId, aOwner, aFeatureStateModel,
                properties.getAutoCompleteMaxResults(), aHandler);
    }
    
    @Override
    public StringFeatureTraits readTraits(AnnotationFeature aFeature)
    {
        StringFeatureTraits traits = null;
        try {
            traits = JSONUtil.fromJsonString(StringFeatureTraits.class, aFeature.getTraits());
        }
        catch (IOException e) {
            log.error("Unable to read traits", e);
        }
    
        if (traits == null) {
            traits = new StringFeatureTraits();
        }
    
        return traits;
    }
    
    @Override
    public void writeTraits(AnnotationFeature aFeature, StringFeatureTraits aTraits)
    {
        try {
            aFeature.setTraits(JSONUtil.toJsonString(aTraits));
        }
        catch (IOException e) {
            log.error("Unable to write traits", e);
        }
    }
}<commit_msg>#1531 - Support keyboard shortcuts<commit_after>/*
 * Copyright 2019
 * Ubiquitous Knowledge Processing (UKP) Lab and FG Language Technology
 * Technische Universität Darmstadt
 *
 * Licensed 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 de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature;

import static java.util.Arrays.asList;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

import org.apache.uima.cas.CAS;
import org.apache.wicket.MarkupContainer;
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 org.springframework.stereotype.Component;

import de.tudarmstadt.ukp.clarin.webanno.api.AnnotationSchemaService;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.action.AnnotationActionHandler;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.config.PrimitiveUimaFeatureSupportProperties;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.FeatureEditor;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.InputFieldTextFeatureEditor;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.KendoAutoCompleteTextFeatureEditor;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.KendoComboboxTextFeatureEditor;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.StringFeatureTraits;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.StringFeatureTraitsEditor;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.feature.editor.TextAreaFeatureEditor;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.model.AnnotatorState;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.model.FeatureState;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer;
import de.tudarmstadt.ukp.clarin.webanno.model.MultiValueMode;
import de.tudarmstadt.ukp.clarin.webanno.model.Tag;
import de.tudarmstadt.ukp.clarin.webanno.support.JSONUtil;

@Component
public class StringFeatureSupport
    extends UimaPrimitiveFeatureSupport_ImplBase<StringFeatureTraits>
{
    private final Logger log = LoggerFactory.getLogger(getClass());
    
    private List<FeatureType> primitiveTypes;

    private final PrimitiveUimaFeatureSupportProperties properties;
    
    private final AnnotationSchemaService schemaService;

    /*
     * Constructor for use in unit tests to avoid having to always instantiate the properties.
     */
    public StringFeatureSupport()
    {
        this(new PrimitiveUimaFeatureSupportProperties(), null);
    }

    @Autowired
    public StringFeatureSupport(PrimitiveUimaFeatureSupportProperties aProperties,
            @Autowired(required = false) AnnotationSchemaService aSchemaService)
    {
        properties = aProperties;
        schemaService = aSchemaService;
    }

    @Override
    public void afterPropertiesSet() throws Exception
    {
        primitiveTypes = asList(
                new FeatureType(CAS.TYPE_NAME_STRING, "Primitive: String", getId()));
    }
    
    @Override
    public List<FeatureType> getSupportedFeatureTypes(AnnotationLayer aAnnotationLayer)
    {
        return Collections.unmodifiableList(primitiveTypes);
    }

    @Override
    public boolean accepts(AnnotationFeature aFeature)
    {
        return MultiValueMode.NONE.equals(aFeature.getMultiValueMode())
                && CAS.TYPE_NAME_STRING.equals(aFeature.getType());
    }

    @Override
    public void setFeatureValue(CAS aCas, AnnotationFeature aFeature, int aAddress, Object aValue)
    {
        if (
                aValue != null &&
                schemaService != null && 
                aFeature.getTagset() != null && 
                CAS.TYPE_NAME_STRING.equals(aFeature.getType()) && 
                !schemaService.existsTag((String) aValue, aFeature.getTagset())
        ) {
            if (!aFeature.getTagset().isCreateTag()) {
                throw new IllegalArgumentException("[" + aValue
                        + "] is not in the tag list. Please choose from the existing tags");
            }
            else {
                Tag selectedTag = new Tag();
                selectedTag.setName((String) aValue);
                selectedTag.setTagSet(aFeature.getTagset());
                schemaService.createTag(selectedTag);
            }
        }
        
        super.setFeatureValue(aCas, aFeature, aAddress, aValue);
    }
    
    @Override
    public Panel createTraitsEditor(String aId,  IModel<AnnotationFeature> aFeatureModel)
    {
        AnnotationFeature feature = aFeatureModel.getObject();
        
        if (!accepts(feature)) {
            throw unsupportedFeatureTypeException(feature);
        }
        
        return new StringFeatureTraitsEditor(aId, this, aFeatureModel);
    }
    
    @Override
    public FeatureEditor createEditor(String aId, MarkupContainer aOwner,
            AnnotationActionHandler aHandler, final IModel<AnnotatorState> aStateModel,
            final IModel<FeatureState> aFeatureStateModel)
    {
        AnnotationFeature feature = aFeatureStateModel.getObject().feature;

        if (!accepts(feature)) {
            throw unsupportedFeatureTypeException(feature);
        }

        StringFeatureTraits traits = readTraits(feature);
        if (feature.getTagset() == null) {
            if (traits.isMultipleRows()) {
                // If multiple rows are set use a textarea
                return new TextAreaFeatureEditor(aId, aOwner, aFeatureStateModel);
            }
            else {
                // Otherwise use a simple input field
                return new InputFieldTextFeatureEditor(aId, aOwner, aFeatureStateModel, aHandler);
            }
        }

        if (aFeatureStateModel.getObject().tagset.size() < properties.getAutoCompleteThreshold()) {
            // For smaller tagsets, use a combobox
            return new KendoComboboxTextFeatureEditor(aId, aOwner, aFeatureStateModel, aHandler);
        }

        // For larger ones, use an auto-complete field
        return new KendoAutoCompleteTextFeatureEditor(aId, aOwner, aFeatureStateModel,
                properties.getAutoCompleteMaxResults(), aHandler);
    }
    
    @Override
    public StringFeatureTraits readTraits(AnnotationFeature aFeature)
    {
        StringFeatureTraits traits = null;
        try {
            traits = JSONUtil.fromJsonString(StringFeatureTraits.class, aFeature.getTraits());
        }
        catch (IOException e) {
            log.error("Unable to read traits", e);
        }
    
        if (traits == null) {
            traits = new StringFeatureTraits();
        }
    
        return traits;
    }
    
    @Override
    public void writeTraits(AnnotationFeature aFeature, StringFeatureTraits aTraits)
    {
        try {
            aFeature.setTraits(JSONUtil.toJsonString(aTraits));
        }
        catch (IOException e) {
            log.error("Unable to write traits", e);
        }
    }
    
    @Override
    public boolean suppressAutoFocus(AnnotationFeature aFeature)
    {
        StringFeatureTraits traits = readTraits(aFeature);
        return !traits.getKeyBindings().isEmpty();
    }
}
{'fraction_non_alphanumeric': 0.08538238141335915, 'fraction_numerical': 0.001548886737657309, 'mean_word_length': 3.0124287933713103, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3963037', 'n_tokens_mistral': 4284, 'n_tokens_neox': 4197, 'n_words': 887}