001/*
002 * (C) Copyright 2018 Nuxeo (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 *     Funsho David
018 */
019
020package org.nuxeo.ecm.platform.csv.export.io;
021
022import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.CHANGE_TOKEN_FIELD;
023import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.IS_CHECKED_OUT_FIELD;
024import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.IS_PROXY_FIELD;
025import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.IS_TRASHED_FIELD;
026import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.IS_VERSION_FIELD;
027import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.LAST_MODIFIED_FIELD;
028import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.LOCK_CREATED_FIELD;
029import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.LOCK_OWNER_FIELD;
030import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.PARENT_REF_FIELD;
031import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.PATH_FIELD;
032import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.PROXY_TARGET_ID_FIELD;
033import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.REPOSITORY_FIELD;
034import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.STATE_FIELD;
035import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.TITLE_FIELD;
036import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.TYPE_FIELD;
037import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.UID_FIELD;
038import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.VERSIONABLE_ID_FIELD;
039import static org.nuxeo.ecm.core.io.marshallers.csv.CSVMarshallerConstants.VERSION_LABEL_FIELD;
040
041import java.io.IOException;
042import java.util.ArrayList;
043import java.util.Arrays;
044import java.util.Collections;
045import java.util.Comparator;
046import java.util.List;
047import java.util.Objects;
048import java.util.stream.Collectors;
049
050import org.apache.commons.csv.CSVPrinter;
051import org.apache.commons.lang3.StringUtils;
052import org.nuxeo.ecm.core.io.registry.context.RenderingContext;
053import org.nuxeo.ecm.core.schema.SchemaManager;
054import org.nuxeo.ecm.core.schema.types.Field;
055import org.nuxeo.ecm.core.schema.types.ListType;
056import org.nuxeo.ecm.core.schema.types.Schema;
057import org.nuxeo.ecm.core.schema.types.Type;
058import org.nuxeo.ecm.core.schema.types.resolver.ObjectResolver;
059import org.nuxeo.ecm.directory.Directory;
060import org.nuxeo.ecm.directory.DirectoryEntryResolver;
061import org.nuxeo.runtime.api.Framework;
062
063/**
064 * Utility class to have helper methods for exporting a document model in a CSV file.
065 *
066 * @since 10.3
067 */
068public class DocumentModelCSVHelper {
069
070    public static final String[] SYSTEM_PROPERTIES_HEADER_FIELDS = new String[] { REPOSITORY_FIELD, UID_FIELD,
071            PATH_FIELD, TYPE_FIELD, STATE_FIELD, PARENT_REF_FIELD, IS_CHECKED_OUT_FIELD, IS_VERSION_FIELD,
072            IS_PROXY_FIELD, PROXY_TARGET_ID_FIELD, VERSIONABLE_ID_FIELD, CHANGE_TOKEN_FIELD, IS_TRASHED_FIELD,
073            TITLE_FIELD, VERSION_LABEL_FIELD, LOCK_OWNER_FIELD, LOCK_CREATED_FIELD, LAST_MODIFIED_FIELD };
074
075    public static final List<String> VOCABULARY_TYPES = Arrays.asList("vocabulary", "xvocabulary", "l10nxvocabulary");
076
077    /**
078     * Prints the fields for document model system properties.
079     */
080    public static void printSystemPropertiesHeader(CSVPrinter printer) throws IOException {
081        for (String header : SYSTEM_PROPERTIES_HEADER_FIELDS) {
082            printer.print(header);
083        }
084    }
085
086    /**
087     * Prints the fields for document model properties of given schemas and xpaths.
088     */
089    public static void printPropertiesHeader(List<String> schemas, List<String> xpaths, CSVPrinter printer)
090            throws IOException {
091        SchemaManager schemaManager = Framework.getService(SchemaManager.class);
092        for (String schemaName : schemas) {
093            Schema schema = schemaManager.getSchema(schemaName);
094            if (schema != null) {
095                List<Field> fields = new ArrayList<>(schema.getFields());
096                fields.sort(Comparator.comparing(o -> o.getName().getLocalName()));
097                String prefix = schema.getNamespace().prefix;
098                if (StringUtils.isBlank(prefix)) {
099                    prefix = schema.getName();
100                }
101                prefix += ":";
102                for (Field f : fields) {
103                    String prefixedName = prefix + f.getName().getLocalName();
104                    printer.print(prefixedName);
105                    if (isVocabulary(f.getType())) {
106                        printer.print(prefixedName + "[label]");
107                    }
108                }
109            }
110        }
111        for (String xpath : xpaths) {
112            Field field = schemaManager.getField(xpath);
113            if (field != null) {
114                printer.print(xpath);
115                if (isVocabulary(field.getType())) {
116                    printer.print(xpath + "[label]");
117                }
118            }
119        }
120    }
121
122    /**
123     * Checks if given type is a vocabulary.
124     */
125    public static boolean isVocabulary(Type type) {
126        if (type instanceof ListType) {
127            type = ((ListType) type).getFieldType();
128        }
129        ObjectResolver resolver = type.getObjectResolver();
130        if (resolver instanceof DirectoryEntryResolver) {
131            DirectoryEntryResolver directoryEntryResolver = (DirectoryEntryResolver) resolver;
132            return VOCABULARY_TYPES.contains(directoryEntryResolver.getDirectory().getSchema());
133        }
134        return false;
135    }
136
137    public static Directory getVocabulary(Type type) {
138        if (type instanceof ListType) {
139            type = ((ListType) type).getFieldType();
140        }
141        if (isVocabulary(type)) {
142            return ((DirectoryEntryResolver) type.getObjectResolver()).getDirectory();
143        }
144        return null;
145    }
146
147    @SuppressWarnings("unchecked")
148    public static List<String> getList(RenderingContext ctx, String key) {
149        Object value = ctx.getParameter(key);
150        if (value == null) {
151            return Collections.emptyList();
152        }
153        return ((List<String>) value).stream().filter(Objects::nonNull).collect(Collectors.toList());
154    }
155
156    private DocumentModelCSVHelper() {
157        // utility class
158    }
159}