001/*
002 * (C) Copyright 2006-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 */
017package org.nuxeo.ecm.diff.content.adapter;
018
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.Map;
022import java.util.Set;
023import java.util.function.Consumer;
024import java.util.stream.Collectors;
025
026import org.apache.commons.lang3.StringUtils;
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.ecm.core.api.DocumentModel;
030import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
031import org.nuxeo.ecm.diff.content.ContentDiffAdapter;
032import org.nuxeo.ecm.diff.content.ContentDiffException;
033import org.nuxeo.ecm.diff.content.MimeTypeDescriptor;
034import org.nuxeo.ecm.diff.content.MimeTypesDescriptor;
035import org.nuxeo.ecm.diff.content.adapter.factories.BlobHolderContentDiffAdapterFactory;
036import org.nuxeo.ecm.diff.content.adapter.factories.FileBasedContentDiffAdapterFactory;
037import org.nuxeo.runtime.model.ComponentInstance;
038import org.nuxeo.runtime.model.DefaultComponent;
039
040/**
041 * Runtime component that handles the extension points and the service interface for content diff Adapter management.
042 *
043 * @author Antoine Taillefer
044 */
045public class ContentDiffAdapterManagerComponent extends DefaultComponent implements ContentDiffAdapterManager {
046
047    public static final String ADAPTER_FACTORY_EP = "adapterFactory";
048
049    public static final String MIME_TYPE_CONTENT_DIFFER_EP = "mimeTypeContentDiffer";
050
051    /**
052     * @since 10.10
053     */
054    public static final String HTML_CONVERSION_BLACKLISTED_MIME_TYPES_EP = "htmlConversionBlacklistedMimeTypes";
055
056    private static final Log log = LogFactory.getLog(ContentDiffAdapterManagerComponent.class);
057
058    protected Map<String, ContentDiffAdapterFactory> factoryRegistry = new HashMap<>();
059
060    protected Map<String, MimeTypeContentDiffer> contentDifferFactory = new HashMap<>();
061
062    protected Map<String, MimeTypeContentDiffer> contentDifferFactoryByName = new HashMap<>();
063
064    protected Set<String> htmlConversionBlacklistedMimeTypes = new HashSet<>();
065
066    // Component and EP management
067
068    @Override
069    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
070
071        if (ADAPTER_FACTORY_EP.equals(extensionPoint)) {
072            ContentDiffAdapterFactoryDescriptor desc = (ContentDiffAdapterFactoryDescriptor) contribution;
073            if (desc.isEnabled()) {
074                ContentDiffAdapterFactory factory = desc.getNewInstance();
075                if (factory != null) {
076                    factoryRegistry.put(desc.getTypeName(), factory);
077                }
078            } else {
079                factoryRegistry.remove(desc.getTypeName());
080            }
081        } else if (MIME_TYPE_CONTENT_DIFFER_EP.equals(extensionPoint)) {
082            MimeTypeContentDifferDescriptor desc = (MimeTypeContentDifferDescriptor) contribution;
083            try {
084                MimeTypeContentDiffer contentDiffer = desc.getKlass().newInstance();
085                contentDifferFactory.put(desc.getPattern(), contentDiffer);
086
087                // Also (since 7.4) add a name in the contribution
088                String name = desc.getName();
089                if (StringUtils.isNotBlank(name)) {
090                    contentDifferFactoryByName.put(name, contentDiffer);
091                }
092            } catch (ReflectiveOperationException e) {
093                throw new RuntimeException(e);
094            }
095        } else if (HTML_CONVERSION_BLACKLISTED_MIME_TYPES_EP.equals(extensionPoint)) {
096            MimeTypesDescriptor desc = (MimeTypesDescriptor) contribution;
097            if (desc.isOverride()) {
098                // override the whole list
099                htmlConversionBlacklistedMimeTypes = desc.getMimeTypes()
100                                                         .stream()
101                                                         .filter(MimeTypeDescriptor::isEnabled)
102                                                         .map(MimeTypeDescriptor::getName)
103                                                         .collect(Collectors.toSet());
104            } else {
105                desc.getMimeTypes().forEach(mimeType -> {
106                    Consumer<String> consumer = mimeType.isEnabled() ? htmlConversionBlacklistedMimeTypes::add
107                            : htmlConversionBlacklistedMimeTypes::remove;
108                    consumer.accept(mimeType.getName());
109                });
110            }
111        }
112    }
113
114    @Override
115    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
116    }
117
118    // Service interface impl
119
120    @Override
121    public boolean hasAdapter(DocumentModel doc) {
122        if (doc == null) {
123            return false;
124        }
125
126        String docType = doc.getType();
127        if (factoryRegistry.containsKey(docType)) {
128            return true;
129        }
130
131        return doc.hasSchema("file") || doc.hasSchema("files");
132    }
133
134    @Override
135    public ContentDiffAdapter getAdapter(DocumentModel doc) {
136        if (doc == null) {
137            return null;
138        }
139
140        String docType = doc.getType();
141
142        log.debug("Looking for ContentDiffAdapter for type " + docType);
143
144        if (factoryRegistry.containsKey(docType)) {
145            log.debug("Dedicated ContentDiffAdapter factory found");
146            return factoryRegistry.get(docType).getAdapter(doc);
147        }
148
149        if (doc.isFolder()) {
150            return null;
151        }
152
153        BlobHolder bh = doc.getAdapter(BlobHolder.class);
154        if (bh != null) {
155            log.debug("Using Blob Holder based ContentDiffAdapter factory");
156            ContentDiffAdapterFactory factory = new BlobHolderContentDiffAdapterFactory();
157            return factory.getAdapter(doc);
158
159        }
160
161        if (doc.hasSchema("file") || doc.hasSchema("files")) {
162            log.debug("Using default file based ContentDiffAdapter factory");
163            ContentDiffAdapterFactory factory = new FileBasedContentDiffAdapterFactory();
164            return factory.getAdapter(doc);
165        } else {
166            return null;
167        }
168    }
169
170    @Override
171    public MimeTypeContentDiffer getContentDiffer(String mimeType) {
172        for (Map.Entry<String, MimeTypeContentDiffer> entry : contentDifferFactory.entrySet()) {
173            if (mimeType.matches(entry.getKey())) {
174                return entry.getValue();
175            }
176        }
177        return null;
178    }
179
180    @Override
181    public MimeTypeContentDiffer getContentDifferForName(String name) {
182        for (Map.Entry<String, MimeTypeContentDiffer> entry : contentDifferFactoryByName.entrySet()) {
183            if (name.equals(entry.getKey())) {
184                return entry.getValue();
185            }
186        }
187        return null;
188    }
189
190    @Override
191    public HtmlContentDiffer getHtmlContentDiffer() throws ContentDiffException {
192        MimeTypeContentDiffer htmlContentDiffer = contentDifferFactory.get("text/html");
193        if (htmlContentDiffer == null || !(htmlContentDiffer instanceof HtmlContentDiffer)) {
194            throw new ContentDiffException(
195                    "No content differ of type HtmlContentDiffer found for the 'text/html' mime-type. Please check the 'mimeTypeContentDiffer' contributions.");
196        }
197        return (HtmlContentDiffer) htmlContentDiffer;
198    }
199
200    @Override
201    public Set<String> getHtmlConversionBlacklistedMimeTypes() {
202        return htmlConversionBlacklistedMimeTypes;
203    }
204
205}