001/*
002 * (C) Copyright 2006-2015 Nuxeo SA (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-2.1.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 *     Nuxeo - initial API and implementation
016 *
017 */
018
019package org.nuxeo.runtime.deployment.preprocessor.template;
020
021import java.util.LinkedHashMap;
022import java.util.Map;
023import java.util.regex.Matcher;
024import java.util.regex.Pattern;
025
026import org.apache.commons.logging.Log;
027import org.apache.commons.logging.LogFactory;
028import org.nuxeo.common.utils.TextTemplate;
029
030/**
031 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
032 */
033public class Template {
034
035    private static final Log log = LogFactory.getLog(Template.class);
036
037    public static final String BEGIN = "BEGIN";
038
039    public static final String END = "END";
040
041    protected static final String JBOSS5_COMPAT = "org.nuxeo.runtme.preprocessing.jboss5";
042
043    // we should use a linked hash map to preserve the
044    // insertion order when iterating over the elements in the map
045    final LinkedHashMap<String, Part> parts;
046
047    /**
048     * Must be removed when fixing deployment-fragment.xml files.
049     */
050    protected boolean runningOnJBoss5 = false;
051
052    public Template() {
053        parts = new LinkedHashMap<>();
054        // XXX compat code
055        String v = System.getProperty(JBOSS5_COMPAT);
056        if (v != null) {
057            runningOnJBoss5 = Boolean.parseBoolean(v);
058        }
059    }
060
061    public void addPart(String name, String text) {
062        parts.put(name, new Part(name, text));
063    }
064
065    public void update(TemplateContribution tc, Map<String, String> ctx) {
066        String content = getContent(tc, ctx);
067        content = new TextTemplate(ctx).processText(content);
068        if (tc.isAppending()) {
069            appendText(tc.getMarker(), content);
070        } else if (tc.isPrepending()) {
071            prependText(tc.getMarker(), content);
072        } else if (tc.isReplacing()) {
073            replaceText(tc.getMarker(), content);
074        }
075    }
076
077    public void appendText(String marker, String text) {
078        Part part = parts.get(marker);
079        if (part != null) {
080            part.append(text);
081        } else {
082            log.debug("Could not find marker: " + marker);
083        }
084    }
085
086    public void prependText(String marker, String text) {
087        Part part = parts.get(marker);
088        if (part != null) {
089            part.prepend(text);
090        } else {
091            log.debug("Could not find marker: " + marker);
092        }
093    }
094
095    public void replaceText(String marker, String text) {
096        Part part = parts.get(marker);
097        if (part != null) {
098            part.replace(text);
099        } else {
100            log.debug("Could not find marker: " + marker);
101        }
102    }
103
104    public String getText() {
105        StringBuilder buf = new StringBuilder();
106        for (Part part : parts.values()) {
107            buf.append(part.text);
108        }
109        return buf.toString();
110    }
111
112    static class Part {
113        public final String name; // the name of the part used in markers
114
115        public final StringBuffer text; // the text before the marker
116
117        public final int offset; // the initial length of the text
118
119        Part(String name, String text) {
120            this.name = name;
121            this.text = text == null ? new StringBuffer() : new StringBuffer(text);
122            offset = this.text.length();
123        }
124
125        public void append(String aText) {
126            text.append(aText);
127        }
128
129        public void prepend(String aText) {
130            text.insert(offset, aText);
131        }
132
133        public void replace(String aText) {
134            text.replace(offset, text.length(), aText);
135        }
136
137        public String getText() {
138            return text.toString();
139        }
140
141        public String getName() {
142            return name;
143        }
144
145    }
146
147    /*
148     * TODO: Remove the following methods when deployment-fragment.xml files will be fixed. These files must not contain
149     * <module><java>...</java></module> declarations.
150     */
151
152    /**
153     * Wrapper method introduced to fix JEE java modules in application template. XXX When this will be solved in trunk
154     * you can remove this method and simply call {@code tc.getContent();}.
155     */
156    protected String getContent(TemplateContribution tc, Map<String, String> context) {
157        String content = tc.getContent();
158        if (runningOnJBoss5 && "application".equals(tc.getTemplate()) && "MODULE".equals(tc.getMarker())) {
159            // remove JEE java modules
160            String oldcontent = content;
161            content = removeJavaModules(content);
162            if (oldcontent != content) {
163                log.warn("The deployment-fragment contains illegal JEE java module contribution: "
164                        + context.get("bundle.shortName"));
165            }
166            if (content.length() == 0) {
167                return "";
168            }
169        }
170        return "\n" + content.trim() + "\n";
171    }
172
173    protected static final Pattern JAVA_MODULE = Pattern.compile("<\\s*module\\s*>\\s*<\\s*java\\s*>.+<\\s*/\\s*java\\s*>\\s*<\\s*/\\s*module\\s*>");
174
175    /**
176     * Remove {@code <module><java>...</java></module>} from {@code application.xml} contributions. This a temporary fix
177     * to remove incorrect java module declarations from deployment-fragments - this should be fixed in each fragment.
178     */
179    protected static String removeJavaModules(String content) {
180        Matcher m = JAVA_MODULE.matcher(content);
181        if (m.find()) {
182            return m.replaceAll("").trim();
183        }
184        return content;
185    }
186
187}