001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nicolas Chapurlat
018 */
019package org.nuxeo.ecm.platform.spreadsheet;
020
021import static org.nuxeo.ecm.core.io.registry.reflect.Instantiations.SINGLETON;
022import static org.nuxeo.ecm.core.io.registry.reflect.Priorities.REFERENCE;
023
024import java.io.IOException;
025import java.util.ArrayList;
026import java.util.HashMap;
027import java.util.List;
028import java.util.Map;
029
030import javax.inject.Inject;
031
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.codehaus.jackson.JsonGenerationException;
035import org.codehaus.jackson.JsonGenerator;
036import org.nuxeo.ecm.core.api.DocumentModel;
037import org.nuxeo.ecm.core.api.model.Property;
038import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
039import org.nuxeo.ecm.core.io.marshallers.json.enrichers.AbstractJsonEnricher;
040import org.nuxeo.ecm.core.io.registry.reflect.Setup;
041import org.nuxeo.ecm.core.schema.SchemaManager;
042import org.nuxeo.ecm.core.schema.types.Field;
043import org.nuxeo.ecm.core.schema.types.Schema;
044import org.nuxeo.ecm.directory.DirectoryException;
045import org.nuxeo.ecm.directory.Session;
046import org.nuxeo.ecm.directory.api.DirectoryService;
047
048@Setup(mode = SINGLETON, priority = REFERENCE)
049public class DCVocabulariesJsonEnricher extends AbstractJsonEnricher<DocumentModel> {
050
051    private static final Log log = LogFactory.getLog(DCVocabulariesJsonEnricher.class);
052
053    public static final String NAME = "vocabularies";
054
055    private static final String DIRECTORY_DEFAULT_LABEL_PREFIX = "label_";
056
057    private static final String KEY_SEPARATOR = "/";
058
059    @Inject
060    private DirectoryService directoryService;
061
062    @Inject
063    private SchemaManager schemaManager;
064
065    public DCVocabulariesJsonEnricher() {
066        super(NAME);
067    }
068
069    @Override
070    public void write(JsonGenerator jg, DocumentModel document) throws IOException {
071        writeVocabulary(jg, document, "l10nsubjects", "dc:subjects");
072        writeVocabulary(jg, document, "l10ncoverage", "dc:coverage");
073    }
074
075    private void writeVocabulary(JsonGenerator jg, final DocumentModel doc, String directoryName, String fieldName)
076            throws IOException, JsonGenerationException {
077        try {
078            // Lookup directory schema to find label columns
079            List<String> labelFields = getLabelFields(directoryName);
080            // Get the field values
081            String[] entriesIds = getPropertyValues(doc, fieldName);
082            // 'field': [
083            jg.writeFieldName(fieldName);
084            jg.writeStartArray();
085            // { 'id': ..., 'label_*': ... }
086            if (entriesIds != null) {
087                writeLabels(jg, directoryName, entriesIds, labelFields);
088            }
089            // ]
090            jg.writeEndArray();
091        } catch (PropertyNotFoundException | DirectoryException e) {
092            log.error(e.getMessage());
093        }
094    }
095
096    /**
097     * Writes the labels for each entry
098     *
099     * @param jg
100     * @param directoryName
101     * @param entriesIds
102     * @param labelFields
103     * @throws IOException
104     */
105    private void writeLabels(JsonGenerator jg, String directoryName, String[] entriesIds, List<String> labelFields)
106            throws IOException {
107        try (Session session = directoryService.open(directoryName)) {
108            for (String entryId : entriesIds) {
109                Map<String, String> labels = getAbsoluteLabels(entryId, session, labelFields);
110                // Write absolute labels (<parent label> / <child label>)
111                jg.writeStartObject();
112                jg.writeStringField("id", entryId);
113                for (Map.Entry<String, String> label : labels.entrySet()) {
114                    jg.writeStringField(label.getKey(), label.getValue());
115                }
116                jg.writeEndObject();
117            }
118        }
119    }
120
121    /**
122     * Determines label columns based on the label prefix
123     *
124     * @param directoryName the name of the directory to inspect
125     * @return
126     */
127    private List<String> getLabelFields(String directoryName) {
128        String schemaName = directoryService.getDirectorySchema(directoryName);
129        Schema schema = schemaManager.getSchema(schemaName);
130        List<String> labelFields = new ArrayList<String>();
131        String fieldName;
132        for (Field field : schema.getFields()) {
133            fieldName = field.getName().toString();
134            if (fieldName.startsWith(DIRECTORY_DEFAULT_LABEL_PREFIX)) {
135                labelFields.add(fieldName);
136            }
137        }
138        return labelFields;
139    }
140
141    /**
142     * Return the values of a document's property as an array of strings
143     *
144     * @param doc
145     * @param fieldName
146     * @return
147     */
148    private static String[] getPropertyValues(DocumentModel doc, String fieldName) {
149        String[] entriesIds = null;
150        Property prop = doc.getProperty(fieldName);
151        if (prop.isList()) {
152            entriesIds = prop.getValue(String[].class);
153        } else {
154            String value = prop.getValue(String.class);
155            if (value != null) {
156                entriesIds = new String[] { value };
157            }
158        }
159        return entriesIds;
160    }
161
162    /**
163     * Returns absolute labels for a given entry (<parent label> / <child label>)
164     *
165     * @param entryId
166     * @param session
167     * @param labelFields
168     * @return a map of field: label
169     */
170    private static Map<String, String> getAbsoluteLabels(final String entryId, final Session session,
171            List<String> labelFields) {
172        String[] split = entryId.split(KEY_SEPARATOR);
173        Map<String, String> labels = new HashMap<>();
174        for (int i = 0; i < split.length; i++) {
175            DocumentModel entry = session.getEntry(split[i]);
176            if (entry == null) {
177                continue;
178            }
179            for (String labelField : labelFields) {
180                String result = labels.get(labelField);
181                if (result == null) {
182                    result = "";
183                }
184                String value = (String) entry.getPropertyValue(labelField);
185                result += (i > 0 ? "/" : "") + value;
186                labels.put(labelField, result);
187            }
188        }
189        return labels;
190    }
191
192}