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 * pierre 018 */ 019package org.nuxeo.ecm.platform.csv.export.io; 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.Arrays; 027import java.util.List; 028import java.util.Locale; 029import java.util.stream.Collectors; 030 031import org.apache.commons.csv.CSVPrinter; 032import org.nuxeo.common.utils.i18n.I18NUtils; 033import org.nuxeo.ecm.core.api.DocumentModel; 034import org.nuxeo.ecm.core.api.model.Property; 035import org.nuxeo.ecm.core.api.model.PropertyNotFoundException; 036import org.nuxeo.ecm.core.api.model.impl.ArrayProperty; 037import org.nuxeo.ecm.core.api.model.impl.primitives.BlobProperty.ScalarMemberProperty; 038import org.nuxeo.ecm.core.io.marshallers.csv.AbstractCSVWriter; 039import org.nuxeo.ecm.core.io.registry.reflect.Setup; 040import org.nuxeo.ecm.core.schema.types.ListType; 041import org.nuxeo.ecm.core.schema.types.Type; 042import org.nuxeo.ecm.core.schema.types.primitives.BinaryType; 043import org.nuxeo.ecm.directory.Directory; 044import org.nuxeo.ecm.directory.Session; 045 046/** 047 * @since 10.3 048 */ 049@Setup(mode = SINGLETON, priority = REFERENCE) 050public class DocumentPropertyCSVWriter extends AbstractCSVWriter<Property> { 051 052 public static final String LIST_DELIMITER = " | "; 053 054 public static final String LANG_CTX_DATA = "lang"; 055 056 public static final String UNKNOWN_TRANSLATED_VALUE_LABEL = "unknown translated value"; 057 058 public DocumentPropertyCSVWriter() { 059 super(); 060 } 061 062 @Override 063 protected void write(Property property, CSVPrinter printer) throws IOException { 064 if (property == null) { 065 printer.print(null); 066 } else if (property.isScalar()) { 067 if (property instanceof ScalarMemberProperty && property.getParent().getValue() == null) { 068 printer.print(null); 069 return; 070 } 071 writeScalarProperty(property, printer); 072 } else if (property.isList()) { 073 writeListProperty(property, printer); 074 } else { 075 writeUnsupported(property.getType(), printer); 076 } 077 } 078 079 @Override 080 protected void writeHeader(Property property, CSVPrinter printer) throws IOException { 081 printer.printRecord(property.getXPath()); 082 } 083 084 protected void writeScalarProperty(Property property, CSVPrinter printer) throws IOException { 085 Object value = property.getValue(); 086 Type type = property.getType(); 087 if (type instanceof BinaryType) { 088 writeUnsupported(type, printer); 089 } else { 090 String valueAsString = null; 091 if (value == null) { 092 printer.print(null); 093 } else { 094 valueAsString = type.encode(value); 095 printer.print(valueAsString); 096 } 097 Directory vocabulary = DocumentModelCSVHelper.getVocabulary(type); 098 if (vocabulary != null) { 099 writeScalarVocabularyProperty(valueAsString, vocabulary, printer); 100 } 101 } 102 } 103 104 protected void writeScalarVocabularyProperty(String value, Directory vocabulary, CSVPrinter printer) 105 throws IOException { 106 if (value == null) { 107 printer.print(null); 108 } else { 109 try (Session session = vocabulary.getSession()) { 110 String lang = ctx.getParameter(LANG_CTX_DATA); 111 printer.print(getTranslatedValue(session, vocabulary.getSchema(), lang, value)); 112 } 113 } 114 } 115 116 protected void writeListProperty(Property property, CSVPrinter printer) throws IOException { 117 ListType type = (ListType) property.getType(); 118 if (property instanceof ArrayProperty) { 119 Object[] array = (Object[]) property.getValue(); 120 Type itemType = type.getFieldType(); 121 Directory vocabulary = DocumentModelCSVHelper.getVocabulary(itemType); 122 if (array == null) { 123 printer.print(null); 124 if (vocabulary != null) { 125 printer.print(null); 126 } 127 return; 128 } 129 if (itemType instanceof BinaryType) { 130 writeUnsupported(type, printer); 131 } else { 132 String value = Arrays.stream(array).map(itemType::encode).collect(Collectors.joining(LIST_DELIMITER)); 133 printer.print(value); 134 if (vocabulary != null) { 135 String[] values = Arrays.stream(array).map(itemType::encode).toArray(String[]::new); 136 writeListVocabularyProperty(values, vocabulary, printer); 137 } 138 } 139 } else { 140 writeUnsupported(type, printer); 141 } 142 } 143 144 protected void writeListVocabularyProperty(String[] values, Directory vocabulary, CSVPrinter printer) 145 throws IOException { 146 List<String> translated = new ArrayList<>(); 147 try (Session session = vocabulary.getSession()) { 148 String lang = ctx.getParameter(LANG_CTX_DATA); 149 for (String value : values) { 150 translated.add(getTranslatedValue(session, vocabulary.getSchema(), lang, value)); 151 } 152 } 153 printer.print(translated.stream().collect(Collectors.joining(LIST_DELIMITER))); 154 } 155 156 protected String getTranslatedValue(Session session, String schema, String lang, String value) { 157 if (value.contains("/")) { 158 value = value.substring(value.lastIndexOf("/") + 1); 159 } 160 DocumentModel entry = session.getEntry(value); 161 if (entry == null) { 162 return UNKNOWN_TRANSLATED_VALUE_LABEL; 163 } 164 165 String label; 166 try { 167 label = (String) entry.getProperty(schema, "label"); 168 } catch (PropertyNotFoundException e) { 169 try { 170 // Check if it comes from a l10nxvocabulary, and return this value if it exists, 171 // or else return the id 172 return (String) entry.getProperty(schema, "label_en"); 173 } catch (PropertyNotFoundException e1) { 174 return value; 175 } 176 } 177 Locale locale = lang != null ? Locale.forLanguageTag(lang) : ctx.getLocale(); 178 return I18NUtils.getMessageString("messages", label, new Object[0], locale); 179 } 180 181 protected void writeUnsupported(Type type, CSVPrinter printer) throws IOException { 182 printer.print(String.format("type %s is not supported", type.getName())); 183 } 184 185}