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