001/*
002 * (C) Copyright 2006-2009 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     troger
016 */
017package org.nuxeo.ecm.platform.annotations.preview;
018
019import java.io.IOException;
020import java.io.InputStreamReader;
021import java.io.Reader;
022import java.util.regex.Matcher;
023import java.util.regex.Pattern;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027import org.jboss.seam.international.LocaleSelector;
028import org.nuxeo.ecm.core.api.Blob;
029import org.nuxeo.ecm.core.api.Blobs;
030import org.nuxeo.ecm.platform.preview.adapter.BlobPostProcessor;
031import org.nuxeo.ecm.platform.web.common.vh.VirtualHostHelper;
032
033/**
034 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a>
035 */
036public class AnnotationBlobPostProcessor implements BlobPostProcessor {
037
038    private static final Log log = LogFactory.getLog(AnnotationBlobPostProcessor.class);
039
040    protected static final int BUFFER_SIZE = 4096 * 16;
041
042    protected static final String GWT_LOCALE = "<meta name=\"gwt:property\" content=\"locale=%s\" />";
043
044    protected static final String ANNOTATION_MODULE_JS = "<script type=\"text/javascript\" src='"
045            + VirtualHostHelper.getContextPathProperty()
046            + "/org.nuxeo.ecm.platform.annotations.gwt.AnnotationFrameModule/org.nuxeo.ecm.platform.annotations.gwt.AnnotationFrameModule.nocache.js'></script>";
047
048    protected static final String INTERNET_EXPLORER_RANGE_JS = "<script type=\"text/javascript\" src='"
049            + VirtualHostHelper.getContextPathProperty() + "/scripts/InternetExplorerRange.js'></script>";
050
051    protected Pattern headPattern = Pattern.compile("(.*)(<head>)(.*)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
052
053    protected Pattern htmlPattern = Pattern.compile("(.*)(<html>)(.*)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
054
055    protected Pattern charsetPattern = Pattern.compile("(.*) charset=(.*?)\"(.*)", Pattern.CASE_INSENSITIVE
056            | Pattern.DOTALL);
057
058    public Blob process(Blob blob) {
059        String mimetype = blob.getMimeType();
060        if (mimetype == null || !mimetype.startsWith("text/")) {
061            // blob does not carry HTML payload hence there is no need to try to
062            // inject HTML metadata
063            return blob;
064        }
065        try {
066            String encoding = null;
067            if (blob.getEncoding() == null) {
068                Matcher m = charsetPattern.matcher(blob.getString());
069                if (m.matches()) {
070                    encoding = m.group(2);
071                }
072            } else {
073                encoding = blob.getEncoding();
074            }
075
076            String blobAsString = getBlobAsString(blob, encoding);
077            String processedBlob = addAnnotationModule(blobAsString);
078
079            blob = Blobs.createBlob(processedBlob, blob.getMimeType(), encoding);
080        } catch (IOException e) {
081            log.debug("Unable to process Blob", e);
082        }
083        return blob;
084    }
085
086    protected String getBlobAsString(Blob blob, String encoding) throws IOException {
087        if (encoding == null) {
088            return blob.getString();
089        }
090        Reader reader = new InputStreamReader(blob.getStream(), encoding);
091        return readString(reader);
092    }
093
094    protected String addAnnotationModule(String blob) {
095        LocaleSelector localeSelector = LocaleSelector.instance();
096        StringBuilder sb = new StringBuilder();
097        Matcher m = headPattern.matcher(blob);
098        if (m.matches()) {
099            sb.append(m.group(1));
100            sb.append(m.group(2));
101            if (localeSelector != null) {
102                sb.append(String.format(GWT_LOCALE, localeSelector.getLocaleString()));
103            }
104            sb.append(INTERNET_EXPLORER_RANGE_JS);
105            sb.append(ANNOTATION_MODULE_JS);
106            sb.append(m.group(3));
107        } else {
108            m = htmlPattern.matcher(blob);
109            if (m.matches()) {
110                sb.append(m.group(1));
111                sb.append(m.group(2));
112                sb.append("<head>");
113                if (localeSelector != null) {
114                    sb.append(String.format(GWT_LOCALE, localeSelector.getLocaleString()));
115                }
116                sb.append(INTERNET_EXPLORER_RANGE_JS);
117                sb.append(ANNOTATION_MODULE_JS);
118                sb.append("</head>");
119                sb.append(m.group(3));
120            } else {
121                log.debug("Unable to inject Annotation module javascript");
122                sb.append(blob);
123            }
124        }
125        return sb.toString();
126    }
127
128    public static String readString(Reader reader) throws IOException {
129        StringBuilder sb = new StringBuilder(BUFFER_SIZE);
130        try {
131            char[] buffer = new char[BUFFER_SIZE];
132            int read;
133            while ((read = reader.read(buffer, 0, BUFFER_SIZE)) != -1) {
134                sb.append(buffer, 0, read);
135            }
136        } finally {
137            if (reader != null) {
138                reader.close();
139            }
140        }
141        return sb.toString();
142    }
143
144}