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