001/*
002 * (C) Copyright 2006-2014 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 */
017package org.nuxeo.ecm.platform.preview.adapter.base;
018
019import java.io.File;
020import java.util.List;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.nuxeo.ecm.core.api.Blob;
026import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
027import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder;
028import org.nuxeo.ecm.core.convert.api.ConversionException;
029import org.nuxeo.ecm.core.convert.api.ConversionService;
030import org.nuxeo.ecm.platform.mimetype.MimetypeDetectionException;
031import org.nuxeo.ecm.platform.mimetype.MimetypeNotFoundException;
032import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry;
033import org.nuxeo.ecm.platform.preview.adapter.MimeTypePreviewer;
034import org.nuxeo.ecm.platform.preview.adapter.PreviewAdapterManager;
035import org.nuxeo.ecm.platform.preview.api.NothingToPreviewException;
036import org.nuxeo.ecm.platform.preview.api.PreviewException;
037import org.nuxeo.runtime.api.Framework;
038
039/**
040 * Base class for preview based on "on the fly" HTML transformers
041 *
042 * @author tiry
043 */
044public class ConverterBasedHtmlPreviewAdapter extends AbstractHtmlPreviewAdapter {
045
046    private static final Log log = LogFactory.getLog(ConverterBasedHtmlPreviewAdapter.class);
047
048    protected String defaultFieldXPath;
049
050    protected MimetypeRegistry mimeTypeService;
051
052    public ConversionService getConversionService() {
053        return Framework.getService(ConversionService.class);
054    }
055
056    @Override
057    protected PreviewAdapterManager getPreviewManager() {
058        return Framework.getService(PreviewAdapterManager.class);
059    }
060
061    protected static String getMimeType(Blob blob) {
062        if (blob == null) {
063            return null;
064        }
065
066        String srcMT = blob.getMimeType();
067        if (srcMT == null || srcMT.startsWith("application/octet-stream")) {
068            // call MT Service
069            try {
070                MimetypeRegistry mtr = Framework.getService(MimetypeRegistry.class);
071                srcMT = mtr.getMimetypeFromFilenameAndBlobWithDefault(blob.getFilename(), blob,
072                        "application/octet-stream");
073                log.debug("mime type service returned " + srcMT);
074            } catch (MimetypeDetectionException e) {
075                log.warn("error while calling Mimetype service", e);
076            }
077        }
078        return srcMT;
079    }
080
081    protected String getMimeType(String xpath) {
082        BlobHolder blobHolder2preview = getBlobHolder2preview(xpath);
083        Blob blob = getBlob2preview(blobHolder2preview);
084        return getMimeType(blob);
085    }
086
087    protected String getDefaultPreviewFieldXPath() {
088        return defaultFieldXPath;
089    }
090
091    public void setDefaultPreviewFieldXPath(String xPath) {
092        defaultFieldXPath = xPath;
093    }
094
095    @Override
096    public List<Blob> getPreviewBlobs() throws PreviewException {
097        return getPreviewBlobs(getDefaultPreviewFieldXPath());
098    }
099
100    @Override
101    public boolean hasPreview(String xpath) {
102        String srcMT = getMimeType(xpath);
103        MimeTypePreviewer mtPreviewer = getPreviewManager().getPreviewer(srcMT);
104        return mtPreviewer != null || getConversionService().getConverterName(srcMT, "text/html") != null;
105    }
106
107    @Override
108    public List<Blob> getPreviewBlobs(String xpath) throws PreviewException {
109        BlobHolder blobHolder2preview = getBlobHolder2preview(xpath);
110        Blob blob2Preview = getBlob2preview(blobHolder2preview);
111
112        String srcMT = getMimeType(xpath);
113        log.debug("Source type for HTML preview =" + srcMT);
114        MimeTypePreviewer mtPreviewer = getPreviewManager().getPreviewer(srcMT);
115        if (mtPreviewer != null) {
116            return mtPreviewer.getPreview(blob2Preview, adaptedDoc);
117        }
118
119        String converterName = getConversionService().getConverterName(srcMT, "text/html");
120        if (converterName == null) {
121            log.debug("No dedicated converter found, using generic");
122            converterName = "any2html";
123        }
124
125        BlobHolder result;
126        try {
127            result = getConversionService().convert(converterName, blobHolder2preview, null);
128            setMimeType(result);
129            return result.getBlobs();
130        } catch (ConversionException e) {
131            throw new PreviewException(e.getMessage(), e);
132        }
133    }
134
135    /**
136     * @param blobHolder2preview
137     * @return
138     * @throws PreviewException
139     * @since 5.7.3
140     */
141    private Blob getBlob2preview(BlobHolder blobHolder2preview) throws PreviewException {
142        Blob blob2Preview = blobHolder2preview.getBlob();
143        if (blob2Preview == null) {
144            throw new NothingToPreviewException("Can not preview a document without blob");
145        } else {
146            return blob2Preview;
147        }
148    }
149
150    /**
151     * Returns a blob holder suitable for a preview.
152     *
153     * @param xpath
154     * @param adaptedDoc
155     * @return
156     * @since 5.7.3
157     */
158    private BlobHolder getBlobHolder2preview(String xpath) {
159        if ((xpath == null) || ("default".equals(xpath))) {
160            return adaptedDoc.getAdapter(BlobHolder.class);
161        } else {
162            return new DocumentBlobHolder(adaptedDoc, xpath);
163        }
164    }
165
166    protected void setMimeType(BlobHolder result) {
167        for (Blob blob : result.getBlobs()) {
168            if ((blob.getMimeType() == null || blob.getMimeType().startsWith("application/octet-stream"))
169                    && blob.getFilename().endsWith("html")) {
170                String mimeTpye = getMimeType(blob);
171                blob.setMimeType(mimeTpye);
172            }
173        }
174    }
175
176    public String getMimeType(File file) throws ConversionException {
177        try {
178            return getMimeTypeService().getMimetypeFromFile(file);
179        } catch (ConversionException e) {
180            throw new ConversionException("Could not get MimeTypeRegistry");
181        } catch (MimetypeNotFoundException e) {
182            return "application/octet-stream";
183        } catch (MimetypeDetectionException e) {
184            return "application/octet-stream";
185        }
186    }
187
188    public MimetypeRegistry getMimeTypeService() throws ConversionException {
189        if (mimeTypeService == null) {
190            mimeTypeService = Framework.getService(MimetypeRegistry.class);
191        }
192        return mimeTypeService;
193    }
194
195    @Override
196    public void cleanup() {
197
198    }
199
200    @Override
201    public boolean cachable() {
202        return true;
203    }
204
205    @Override
206    public boolean hasBlobToPreview() throws PreviewException {
207        String xpath = getDefaultPreviewFieldXPath();
208        Blob blob2Preview;
209        try {
210            blob2Preview = getBlob2preview(getBlobHolder2preview(xpath));
211        } catch (NothingToPreviewException e) {
212            return false;
213        }
214        return blob2Preview != null;
215    }
216
217}