de.tudarmstadt.ukp.clarin.webanno.brat.util.CuratorUtil.java Source code

Java tutorial

Introduction

Here is the source code for de.tudarmstadt.ukp.clarin.webanno.brat.util.CuratorUtil.java

Source

/*******************************************************************************
 * Copyright 2012
 * Ubiquitous Knowledge Processing (UKP) Lab and FG Language Technology
 * Technische Universitt 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.brat.util;

import static de.tudarmstadt.ukp.clarin.webanno.brat.controller.BratAjaxCasUtil.getAddr;
import static de.tudarmstadt.ukp.clarin.webanno.brat.controller.TypeUtil.getAdapter;

import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.uima.UIMAException;
import org.apache.uima.cas.FeatureStructure;
import org.apache.uima.cas.Type;
import org.apache.uima.jcas.JCas;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.codehaus.jackson.JsonGenerator;
import org.springframework.beans.BeansException;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.security.core.context.SecurityContextHolder;

import de.tudarmstadt.ukp.clarin.webanno.api.AnnotationService;
import de.tudarmstadt.ukp.clarin.webanno.api.RepositoryService;
import de.tudarmstadt.ukp.clarin.webanno.api.UserDao;
import de.tudarmstadt.ukp.clarin.webanno.api.WebAnnoConst;
import de.tudarmstadt.ukp.clarin.webanno.brat.annotation.BratAnnotator;
import de.tudarmstadt.ukp.clarin.webanno.brat.annotation.BratAnnotatorModel;
import de.tudarmstadt.ukp.clarin.webanno.brat.controller.BratAjaxCasController;
import de.tudarmstadt.ukp.clarin.webanno.brat.controller.BratAnnotationException;
import de.tudarmstadt.ukp.clarin.webanno.brat.controller.ColoringStrategy;
import de.tudarmstadt.ukp.clarin.webanno.brat.controller.SpanAdapter;
import de.tudarmstadt.ukp.clarin.webanno.brat.controller.TypeAdapter;
import de.tudarmstadt.ukp.clarin.webanno.brat.curation.AnnotationOption;
import de.tudarmstadt.ukp.clarin.webanno.brat.curation.AnnotationSelection;
import de.tudarmstadt.ukp.clarin.webanno.brat.curation.CasDiff;
import de.tudarmstadt.ukp.clarin.webanno.brat.curation.component.SuggestionViewPanel;
import de.tudarmstadt.ukp.clarin.webanno.brat.curation.component.model.AnnotationState;
import de.tudarmstadt.ukp.clarin.webanno.brat.curation.component.model.SuggestionBuilder;
import de.tudarmstadt.ukp.clarin.webanno.brat.curation.component.model.CurationContainer;
import de.tudarmstadt.ukp.clarin.webanno.brat.curation.component.model.CurationUserSegmentForAnnotationDocument;
import de.tudarmstadt.ukp.clarin.webanno.brat.curation.component.model.CurationViewForSourceDocument;
import de.tudarmstadt.ukp.clarin.webanno.brat.message.GetCollectionInformationResponse;
import de.tudarmstadt.ukp.clarin.webanno.brat.message.GetDocumentResponse;
import de.tudarmstadt.ukp.clarin.webanno.brat.project.PreferencesUtil;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationDocument;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationDocumentState;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer;
import de.tudarmstadt.ukp.clarin.webanno.model.Mode;
import de.tudarmstadt.ukp.clarin.webanno.model.SourceDocument;
import de.tudarmstadt.ukp.clarin.webanno.model.User;
import de.tudarmstadt.ukp.clarin.webanno.support.JSONUtil;
import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Sentence;
import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Token;

/**
 * A utility class for the curation AND Correction modules
 *
 * @author Seid Muhie Yimam
 */
public class CuratorUtil {
    private static final Log LOG = LogFactory.getLog(CuratorUtil.class);

    public final static String CURATION_USER = "CURATION_USER";

    /**
     * Get JCAS objects of annotator where {@link CasDiff} will run on it
     *
     * @param aJCases
     *            the JCases.
     * @param aAnnotationDocuments
     *            the annotation documents.
     * @param aRepository
     *            the repository.
     * @param annotationSelectionByUsernameAndAddress
     *            selections by user.
     * @throws UIMAException
     *             hum?
     * @throws ClassNotFoundException
     *             hum?
     * @throws IOException
     *             if an I/O error occurs.
     */
    public static void getCases(Map<String, JCas> aJCases, List<AnnotationDocument> aAnnotationDocuments,
            RepositoryService aRepository,
            Map<String, Map<Integer, AnnotationSelection>> annotationSelectionByUsernameAndAddress)
            throws UIMAException, ClassNotFoundException, IOException {
        for (AnnotationDocument annotationDocument : aAnnotationDocuments) {
            String username = annotationDocument.getUser();
            if (annotationDocument.getState().equals(AnnotationDocumentState.FINISHED)
                    || username.equals(CURATION_USER)) {
                JCas jCas = aRepository.readAnnotationCas(annotationDocument);
                aJCases.put(username, jCas);

                // cleanup annotationSelections
                annotationSelectionByUsernameAndAddress.put(username, new HashMap<Integer, AnnotationSelection>());
            }
        }
    }

    /**
     * Set different attributes for {@link BratAnnotatorModel} that will be used for the
     * {@link CurationViewForSourceDocument}
     *
     * @param aSourceDocument
     *            the source document.
     * @param aRepository
     *            the repository.
     * @param aCurationSegment
     *            the segment.
     * @param aAnnotationService
     *            the annotation service.
     * @return the model.
     * @throws BeansException
     *             hum?
     * @throws IOException
     *             if an I/O error occurs.
     */
    public static BratAnnotatorModel setBratAnnotatorModel(SourceDocument aSourceDocument,
            RepositoryService aRepository, CurationViewForSourceDocument aCurationSegment,
            AnnotationService aAnnotationService, UserDao aUserDao) throws BeansException, IOException {
        User userLoggedIn = aUserDao.get(SecurityContextHolder.getContext().getAuthentication().getName());
        BratAnnotatorModel bratAnnotatorModel = new BratAnnotatorModel();// .getModelObject();
        bratAnnotatorModel.setDocument(aSourceDocument);
        bratAnnotatorModel.setProject(aSourceDocument.getProject());
        bratAnnotatorModel.setUser(userLoggedIn);
        bratAnnotatorModel.setSentenceAddress(aCurationSegment.getSentenceAddress().get(CURATION_USER));
        bratAnnotatorModel.setFirstSentenceAddress(aCurationSegment.getSentenceAddress().get(CURATION_USER));
        bratAnnotatorModel.setLastSentenceAddress(aCurationSegment.getSentenceAddress().get(CURATION_USER));

        bratAnnotatorModel.setSentenceBeginOffset(aCurationSegment.getBegin());
        bratAnnotatorModel.setSentenceEndOffset(aCurationSegment.getEnd());

        bratAnnotatorModel.setMode(Mode.CURATION);
        PreferencesUtil.setAnnotationPreference(userLoggedIn.getUsername(), aRepository, aAnnotationService,
                bratAnnotatorModel, Mode.CURATION);

        LOG.debug("Configured BratAnnotatorModel for user [" + userLoggedIn + "] f:["
                + bratAnnotatorModel.getFirstSentenceAddress() + "] l:["
                + bratAnnotatorModel.getLastSentenceAddress() + "] s:[" + bratAnnotatorModel.getSentenceAddress()
                + "]");

        return bratAnnotatorModel;
    }

    public static void fillLookupVariables(List<AnnotationOption> aAnnotationOptions,
            Map<String, Map<Integer, AnnotationSelection>> aAnnotationSelectionByUsernameAndAddress,
            BratAnnotatorModel bratAnnotatorModel) {
        // fill lookup variable for annotation selections
        for (AnnotationOption annotationOption : aAnnotationOptions) {
            for (AnnotationSelection annotationSelection : annotationOption.getAnnotationSelections()) {
                for (String username : annotationSelection.getAddressByUsername().keySet()) {
                    if ((!username.equals(CURATION_USER) && bratAnnotatorModel.getMode().equals(Mode.CURATION))
                            || (username.equals(CURATION_USER)
                                    && (bratAnnotatorModel.getMode().equals(Mode.AUTOMATION)
                                            || bratAnnotatorModel.getMode().equals(Mode.CORRECTION)))) {
                        Integer address = annotationSelection.getAddressByUsername().get(username);
                        // aAnnotationSelectionByUsernameAndAddress.put(username,
                        // new
                        // HashMap<Integer, AnnotationSelection>());
                        aAnnotationSelectionByUsernameAndAddress.get(username).put(address, annotationSelection);
                    }
                }
            }
        }
    }

    public static void populateCurationSentences(Map<String, JCas> aJCases,
            List<CurationUserSegmentForAnnotationDocument> aSentences, BratAnnotatorModel aBratAnnotatorModel,
            final List<AnnotationOption> aAnnotationOptions,
            Map<String, Map<Integer, AnnotationSelection>> aAnnotationSelectionByUsernameAndAddress,
            AnnotationService aAnnotationService, CurationContainer aCurationContainer) throws IOException {
        List<String> usernamesSorted = new ArrayList<String>(aJCases.keySet());
        Collections.sort(usernamesSorted);
        final int numUsers = usernamesSorted.size();

        final Mode mode = aBratAnnotatorModel.getMode();
        boolean isAutomationMode = mode.equals(Mode.AUTOMATION);
        boolean isCorrectionMode = mode.equals(Mode.CORRECTION);
        boolean isCurationMode = mode.equals(Mode.CURATION);

        String annotatorCasUser;
        switch (mode) {
        case AUTOMATION: // fall-through
        case CORRECTION:
            annotatorCasUser = SecurityContextHolder.getContext().getAuthentication().getName();
            break;
        case CURATION:
            annotatorCasUser = CURATION_USER;
            break;
        default:
            throw new IllegalStateException("Illegal mode [" + mode + "]");
        }

        LOG.debug("mode = [" + mode + "]");
        LOG.debug("all users is  " + usernamesSorted);
        LOG.debug("annotator CAS is for user [" + annotatorCasUser + "]");

        for (String username : usernamesSorted) {
            if ((!username.equals(CURATION_USER) && isCurationMode)
                    || (username.equals(CURATION_USER) && (isAutomationMode || isCorrectionMode))) {
                // WTF?
                final Map<Integer, AnnotationSelection> annotationSelectionByAddress = new HashMap<Integer, AnnotationSelection>();
                for (AnnotationOption annotationOption : aAnnotationOptions) {
                    for (AnnotationSelection annotationSelection : annotationOption.getAnnotationSelections()) {
                        if (annotationSelection.getAddressByUsername().containsKey(username)) {
                            Integer address = annotationSelection.getAddressByUsername().get(username);
                            annotationSelectionByAddress.put(address, annotationSelection);
                        }
                    }
                }

                JCas jCas = aJCases.get(username);
                // Set up coloring strategy
                ColoringStrategy curationColoringStrategy = new ColoringStrategy() {
                    @Override
                    public String getColor(FeatureStructure aFS, String aLabel) {
                        int address = getAddr(aFS);
                        AnnotationSelection annotationSelection = annotationSelectionByAddress.get(address);
                        AnnotationState newState = null;
                        if (mode.equals(Mode.AUTOMATION) || mode.equals(Mode.CORRECTION)) {
                            newState = getCorrectionState(annotationSelection, aAnnotationOptions, numUsers,
                                    address);
                        } else {
                            newState = getCurationState(numUsers, annotationSelection);
                        }

                        return newState.getColorCode();
                    }
                };

                // Create curation view for the current user
                CurationUserSegmentForAnnotationDocument curationUserSegment2 = new CurationUserSegmentForAnnotationDocument();
                curationUserSegment2
                        .setCollectionData(getCollectionInformation(aAnnotationService, aCurationContainer));
                curationUserSegment2.setDocumentResponse(
                        render(jCas, aAnnotationService, aBratAnnotatorModel, curationColoringStrategy));
                curationUserSegment2.setUsername(username);
                curationUserSegment2.setBratAnnotatorModel(aBratAnnotatorModel);
                curationUserSegment2
                        .setAnnotationSelectionByUsernameAndAddress(aAnnotationSelectionByUsernameAndAddress);
                aSentences.add(curationUserSegment2);
            }
        }
    }

    private static String render(JCas aJcas, AnnotationService aAnnotationService,
            BratAnnotatorModel aBratAnnotatorModel, ColoringStrategy aCurationColoringStrategy) throws IOException {
        GetDocumentResponse response = new GetDocumentResponse();

        // Render invisible baseline annotations (sentence, tokens)
        SpanAdapter.renderTokenAndSentence(aJcas, response, aBratAnnotatorModel);

        // Render visible (custom) layers
        for (AnnotationLayer layer : aBratAnnotatorModel.getAnnotationLayers()) {
            if (layer.getName().equals(Token.class.getName()) || layer.getName().equals(Sentence.class.getName())
                    || WebAnnoConst.CHAIN_TYPE.equals(layer.getType())) {
                continue;
            }

            List<AnnotationFeature> features = aAnnotationService.listAnnotationFeature(layer);
            List<AnnotationFeature> invisibleFeatures = new ArrayList<AnnotationFeature>();
            for (AnnotationFeature feature : features) {
                if (!feature.isVisible()) {
                    invisibleFeatures.add(feature);
                }
            }
            features.removeAll(invisibleFeatures);
            TypeAdapter adapter = getAdapter(aAnnotationService, layer);
            adapter.render(aJcas, features, response, aBratAnnotatorModel, aCurationColoringStrategy);
        }

        StringWriter out = new StringWriter();
        JsonGenerator jsonGenerator = JSONUtil.getJsonConverter().getObjectMapper().getJsonFactory()
                .createJsonGenerator(out);
        jsonGenerator.writeObject(response);
        return out.toString();
    }

    private static String getCollectionInformation(AnnotationService aAnnotationService,
            CurationContainer aCurationContainer) throws IOException {
        GetCollectionInformationResponse info = new GetCollectionInformationResponse();
        info.setEntityTypes(BratAjaxCasController.buildEntityTypes(
                aCurationContainer.getBratAnnotatorModel().getAnnotationLayers(), aAnnotationService));

        StringWriter out = new StringWriter();
        JsonGenerator jsonGenerator = JSONUtil.getJsonConverter().getObjectMapper().getJsonFactory()
                .createJsonGenerator(out);
        jsonGenerator.writeObject(info);
        return out.toString();
    }

    private static AnnotationState getCurationState(int numUsers, AnnotationSelection annotationSelection) {
        AnnotationState newState;
        if (annotationSelection == null) {
            newState = AnnotationState.AGREE;

        } else if (annotationSelection.getAddressByUsername().size() == numUsers) {
            newState = AnnotationState.AGREE;

        } else if (annotationSelection.getAddressByUsername().containsKey(CURATION_USER)) {
            newState = AnnotationState.USE;

        } else {
            boolean doNotUse = false;
            for (AnnotationSelection otherAnnotationSelection : annotationSelection.getAnnotationOption()
                    .getAnnotationSelections()) {
                if (otherAnnotationSelection.getAddressByUsername().containsKey(CURATION_USER)) {
                    doNotUse = true;
                    break;
                }
            }
            if (doNotUse) {
                newState = AnnotationState.DO_NOT_USE;
            } else {
                newState = AnnotationState.DISAGREE;

            }
        }
        return newState;
    }

    private static AnnotationState getCorrectionState(AnnotationSelection annotationSelection,
            List<AnnotationOption> aAnnotationOptions, int numUsers, int address) {
        AnnotationOption annotationOption = null;

        for (AnnotationOption annotationOption2 : aAnnotationOptions) {
            for (AnnotationSelection annotationSelection2 : annotationOption2.getAnnotationSelections()) {
                if (annotationSelection2.getAddressByUsername().containsKey(CURATION_USER)
                        && annotationSelection2.getAddressByUsername().get(CURATION_USER).equals(address)) {
                    annotationOption = annotationOption2;
                    break;
                }

            }
        }
        AnnotationState newState = null;
        if (annotationSelection == null) {
            newState = AnnotationState.NOT_SUPPORTED;

        } else if (annotationSelection.getAddressByUsername().size() == numUsers) {
            newState = AnnotationState.AGREE;

        } else if (annotationOption.getAnnotationSelections().size() == 1) {
            newState = AnnotationState.DISAGREE;
        } else {
            newState = AnnotationState.DO_NOT_USE;
        }
        return newState;
    }

    /**
     * @param aTarget
     *            the AJAX target.
     * @param aParent
     *            the parent.
     * @param aCurationContainer
     *            the container.
     * @param aMergeVisualizer
     *            the annotator component.
     * @param aRepository
     *            the repository.
     * @param aAnnotationSelectionByUsernameAndAddress
     *            selections by user.
     * @param aCurationSegment
     *            the segment.
     * @param aAnnotationService
     *            the annotation service.
     * @param aJsonConverter
     *            the JSON converter.
     * @return the correction document in automation/correction mode and the curation document in
     *         curation mode.
     * @throws UIMAException
     *             hum?
     * @throws ClassNotFoundException
     *             hum?
     * @throws IOException
     *             hum?
     * @throws BratAnnotationException
     *             hum?
     */
    public static void updatePanel(AjaxRequestTarget aTarget, SuggestionViewPanel aParent,
            CurationContainer aCurationContainer, BratAnnotator aMergeVisualizer, RepositoryService aRepository,
            Map<String, Map<Integer, AnnotationSelection>> aAnnotationSelectionByUsernameAndAddress,
            CurationViewForSourceDocument aCurationSegment, AnnotationService aAnnotationService, UserDao aUserDao)
            throws UIMAException, ClassNotFoundException, IOException, BratAnnotationException {
        SourceDocument sourceDocument = aCurationContainer.getBratAnnotatorModel().getDocument();
        Map<String, JCas> jCases = new HashMap<String, JCas>();

        // This is the CAS that the user can actively edit
        JCas annotatorCas = getAnnotatorCase(aCurationContainer.getBratAnnotatorModel(), aRepository, aUserDao,
                aAnnotationSelectionByUsernameAndAddress, sourceDocument, jCases);

        // We store the CAS that the user will edit as the "CURATION USER"
        jCases.put(CURATION_USER, annotatorCas);

        // get differing feature structures
        List<Type> entryTypes = SuggestionBuilder.getEntryTypes(annotatorCas,
                aCurationContainer.getBratAnnotatorModel().getAnnotationLayers(), aAnnotationService);
        List<AnnotationOption> annotationOptions = null;
        try {
            annotationOptions = CasDiff.doDiff(entryTypes, jCases, aCurationSegment.getBegin(),
                    aCurationSegment.getEnd());
        } catch (Exception e) {
            throw new CasDiffException(e.getMessage());
        }

        // fill lookup variable for annotation selections
        CuratorUtil.fillLookupVariables(annotationOptions, aAnnotationSelectionByUsernameAndAddress,
                aCurationContainer.getBratAnnotatorModel());

        LinkedList<CurationUserSegmentForAnnotationDocument> sentences = new LinkedList<CurationUserSegmentForAnnotationDocument>();

        BratAnnotatorModel bratAnnotatorModel = aCurationContainer.getBratAnnotatorModel();

        CuratorUtil.populateCurationSentences(jCases, sentences, bratAnnotatorModel, annotationOptions,
                aAnnotationSelectionByUsernameAndAddress, aAnnotationService, aCurationContainer);

        // update sentence list on the right side
        aParent.setModelObject(sentences);
        /*   if (aCurationContainer.getBratAnnotatorModel().getMode().equals(Mode.CURATION)) {
        aMergeVisualizer.setModelObject(bratAnnotatorModel);
        aMergeVisualizer.bratRenderLater(aTarget);
           }*/
        aTarget.add(aParent);
    }

    public static JCas getAnnotatorCase(BratAnnotatorModel aBModel, RepositoryService aRepository, UserDao aUserDao,
            Map<String, Map<Integer, AnnotationSelection>> aAnnotationSelectionByUsernameAndAddress,
            SourceDocument sourceDocument, Map<String, JCas> jCases)
            throws UIMAException, IOException, ClassNotFoundException {
        JCas annotatorCas;
        if (aBModel.getMode().equals(Mode.AUTOMATION) || aBModel.getMode().equals(Mode.CORRECTION)) {
            // If this is a CORRECTION or AUTOMATION project, then we get the CORRECTION document
            // and put it in as the single document to compare with. Basically what we do is that
            // we treat consider this scenario as a curation scenario where the CORRECTION document
            // is the only document we compare with.

            // The CAS the user can edit is the one from the virtual CORRECTION USER
            annotatorCas = aRepository.readCorrectionCas(sourceDocument);

            User user = aUserDao.get(SecurityContextHolder.getContext().getAuthentication().getName());
            AnnotationDocument annotationDocument = aRepository.getAnnotationDocument(sourceDocument, user);
            jCases.put(user.getUsername(), aRepository.readAnnotationCas(annotationDocument));
            aAnnotationSelectionByUsernameAndAddress.put(CURATION_USER,
                    new HashMap<Integer, AnnotationSelection>());
        } else {
            // If this is a true CURATION then we get all the annotation documents from all the
            // active users.

            // The CAS the user can edit is the one from the virtual CURATION USER
            annotatorCas = aRepository.readCurationCas(sourceDocument);

            // Now we get all the other CASes from the repository
            List<AnnotationDocument> annotationDocuments = aRepository.listAnnotationDocuments(sourceDocument);
            for (AnnotationDocument annotationDocument : annotationDocuments) {
                String username = annotationDocument.getUser();
                if (annotationDocument.getState().equals(AnnotationDocumentState.FINISHED)
                        || username.equals(CuratorUtil.CURATION_USER)) {
                    JCas jCas = aRepository.readAnnotationCas(annotationDocument);
                    jCases.put(username, jCas);

                    // cleanup annotationSelections
                    aAnnotationSelectionByUsernameAndAddress.put(username,
                            new HashMap<Integer, AnnotationSelection>());
                }
            }
        }
        return annotatorCas;
    }
}