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