001/*
002 * (C) Copyright 2006-2016 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 * Contributors:
017 *     troger
018 */
019package org.nuxeo.ecm.platform.annotations.preview;
020
021import java.io.IOException;
022import java.io.InputStreamReader;
023import java.io.Reader;
024import java.util.regex.Matcher;
025import java.util.regex.Pattern;
026
027import org.apache.commons.codec.digest.DigestUtils;
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.jboss.seam.international.LocaleSelector;
031import org.nuxeo.ecm.core.api.Blob;
032import org.nuxeo.ecm.core.api.Blobs;
033import org.nuxeo.ecm.platform.preview.adapter.BlobPostProcessor;
034import org.nuxeo.ecm.platform.web.common.vh.VirtualHostHelper;
035
036/**
037 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a>
038 */
039public class AnnotationBlobPostProcessor implements BlobPostProcessor {
040
041    private static final Log log = LogFactory.getLog(AnnotationBlobPostProcessor.class);
042
043    protected static final int BUFFER_SIZE = 4096 * 16;
044
045    protected static final String GWT_LOCALE = "<meta name=\"gwt:property\" content=\"locale=%s\" />";
046
047    protected static final String ANNOTATION_MODULE_JS = "<script type=\"text/javascript\" src='"
048            + VirtualHostHelper.getContextPathProperty()
049            + "/org.nuxeo.ecm.platform.annotations.gwt.AnnotationFrameModule/org.nuxeo.ecm.platform.annotations.gwt.AnnotationFrameModule.nocache.js'></script>";
050
051    protected static final String INTERNET_EXPLORER_RANGE_JS = "<script type=\"text/javascript\" src='"
052            + VirtualHostHelper.getContextPathProperty() + "/scripts/InternetExplorerRange.js'></script>";
053
054    protected Pattern headPattern = Pattern.compile("(.*)(<head>)(.*)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
055
056    protected Pattern htmlPattern = Pattern.compile("(.*)(<html>)(.*)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
057
058    protected Pattern charsetPattern = Pattern.compile("(.*) charset=(.*?)\"(.*)", Pattern.CASE_INSENSITIVE
059            | Pattern.DOTALL);
060
061    public Blob process(Blob blob) {
062        String mimetype = blob.getMimeType();
063        if (mimetype == null || !mimetype.startsWith("text/")) {
064            // blob does not carry HTML payload hence there is no need to try to
065            // inject HTML metadata
066            return blob;
067        }
068        try {
069            String encoding = null;
070            if (blob.getEncoding() == null) {
071                Matcher m = charsetPattern.matcher(blob.getString());
072                if (m.matches()) {
073                    encoding = m.group(2);
074                }
075            } else {
076                encoding = blob.getEncoding();
077            }
078
079            String blobAsString = getBlobAsString(blob, encoding);
080            String processedBlob = addAnnotationModule(blobAsString);
081
082            blob = Blobs.createBlob(processedBlob, blob.getMimeType(), encoding, blob.getFilename());
083            blob.setDigest(DigestUtils.md5Hex(processedBlob));
084        } catch (IOException e) {
085            log.debug("Unable to process Blob", e);
086        }
087        return blob;
088    }
089
090    protected String getBlobAsString(Blob blob, String encoding) throws IOException {
091        if (encoding == null) {
092            return blob.getString();
093        }
094        Reader reader = new InputStreamReader(blob.getStream(), encoding);
095        return readString(reader);
096    }
097
098    protected String addAnnotationModule(String blob) {
099        LocaleSelector localeSelector = LocaleSelector.instance();
100        StringBuilder sb = new StringBuilder();
101        Matcher m = headPattern.matcher(blob);
102        if (m.matches()) {
103            sb.append(m.group(1));
104            sb.append(m.group(2));
105            if (localeSelector != null) {
106                sb.append(String.format(GWT_LOCALE, localeSelector.getLocaleString()));
107            }
108            sb.append(INTERNET_EXPLORER_RANGE_JS);
109            sb.append(ANNOTATION_MODULE_JS);
110            sb.append(m.group(3));
111        } else {
112            m = htmlPattern.matcher(blob);
113            if (m.matches()) {
114                sb.append(m.group(1));
115                sb.append(m.group(2));
116                sb.append("<head>");
117                if (localeSelector != null) {
118                    sb.append(String.format(GWT_LOCALE, localeSelector.getLocaleString()));
119                }
120                sb.append(INTERNET_EXPLORER_RANGE_JS);
121                sb.append(ANNOTATION_MODULE_JS);
122                sb.append("</head>");
123                sb.append(m.group(3));
124            } else {
125                log.debug("Unable to inject Annotation module javascript");
126                sb.append(blob);
127            }
128        }
129        return sb.toString();
130    }
131
132    public static String readString(Reader reader) throws IOException {
133        StringBuilder sb = new StringBuilder(BUFFER_SIZE);
134        try {
135            char[] buffer = new char[BUFFER_SIZE];
136            int read;
137            while ((read = reader.read(buffer, 0, BUFFER_SIZE)) != -1) {
138                sb.append(buffer, 0, read);
139            }
140        } finally {
141            if (reader != null) {
142                reader.close();
143            }
144        }
145        return sb.toString();
146    }
147
148}