001    /* 
002        Licensed to the Apache Software Foundation (ASF) under one
003        or more contributor license agreements.  See the NOTICE file
004        distributed with this work for additional information
005        regarding copyright ownership.  The ASF licenses this file
006        to you under the Apache License, Version 2.0 (the
007        "License"); you may not use this file except in compliance
008        with the License.  You may obtain a copy of the License at
009    
010           http://www.apache.org/licenses/LICENSE-2.0
011    
012        Unless required by applicable law or agreed to in writing,
013        software distributed under the License is distributed on an
014        "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015        KIND, either express or implied.  See the License for the
016        specific language governing permissions and limitations
017        under the License.  
018    */
019    package org.apache.wiki.parser;
020    
021    import java.io.IOException;
022    import java.text.MessageFormat;
023    import java.util.HashMap;
024    import java.util.Map;
025    import java.util.NoSuchElementException;
026    import java.util.ResourceBundle;
027    
028    import org.apache.log4j.Logger;
029    import org.apache.oro.text.regex.MatchResult;
030    import org.apache.oro.text.regex.PatternMatcher;
031    import org.apache.oro.text.regex.Perl5Matcher;
032    import org.apache.wiki.InternalWikiException;
033    import org.apache.wiki.WikiContext;
034    import org.apache.wiki.WikiEngine;
035    import org.apache.wiki.api.engine.PluginManager;
036    import org.apache.wiki.api.exceptions.PluginException;
037    import org.apache.wiki.api.plugin.ParserStagePlugin;
038    import org.apache.wiki.api.plugin.WikiPlugin;
039    import org.apache.wiki.preferences.Preferences;
040    import org.apache.wiki.render.RenderingManager;
041    import org.jdom2.Text;
042    
043    
044    /**
045     * Stores the contents of a plugin in a WikiDocument DOM tree.
046     * <p/>
047     * If the RenderingManager.WYSIWYG_EDITOR_MODE is set to Boolean.TRUE in the context,
048     * then the plugin
049     * is rendered as WikiMarkup.  This allows an HTML editor to work without
050     * rendering the plugin each time as well.
051     * <p/>
052     * If RenderingManager.VAR_EXECUTE_PLUGINS is set to Boolean.FALSE, then
053     * the plugin is not executed.
054     *
055     * @since 2.4
056     */
057    public class PluginContent extends Text {
058    
059        private static final String BLANK = "";
060        private static final String CMDLINE = "_cmdline";
061        private static final String ELEMENT_BR = "<br/>";
062        private static final String EMITTABLE_PLUGINS = "Image|FormOpen|FormClose|FormInput|FormTextarea|FormSelect";
063        private static final String LINEBREAK = "\n";
064        private static final String PLUGIN_START = "[{";
065        private static final String PLUGIN_END = "}]";
066        private static final String SPACE = " ";
067    
068        private static final long serialVersionUID = 1L;
069    
070        private static Logger log = Logger.getLogger(PluginContent.class);
071    
072        private String m_pluginName;
073        private Map<String, String> m_params;
074    
075        /**
076         * Creates a new DOM element with the given plugin name and a map of parameters.
077         *
078         * @param pluginName The FQN of a plugin.
079         * @param parameters A Map of parameters.
080         */
081        public PluginContent(String pluginName, Map<String, String> parameters) {
082            m_pluginName = pluginName;
083            m_params = parameters;
084        }
085    
086        /**
087         * Returns the name of the plugin invoked by the DOM element.
088         *
089         * @return Name of the plugin
090         * @since 2.5.7
091         */
092        public String getPluginName() {
093            return m_pluginName;
094        }
095    
096        /**
097         * Returns a parameter value from the parameter map.
098         *
099         * @param name the name of the parameter.
100         * @return The value from the map, or null, if no such parameter exists.
101         */
102        public String getParameter(String name) {
103            return m_params.get(name);
104        }
105    
106        /**
107         * Returns the parameter map given in the constructor.
108         *
109         * @return The parameter map.
110         */
111        public Map<String, String> getParameters() {
112            return m_params;
113        }
114    
115        /**
116         * Returns the rendered plugin.  Only calls getText().
117         *
118         * @return HTML
119         */
120        public String getValue() {
121            return getText();
122        }
123    
124        /**
125         * The main invocation for the plugin.  When the getText() is called, it
126         * invokes the plugin and returns its contents.  If there is no Document
127         * yet, only returns the plugin name itself.
128         *
129         * @return The plugin rendered according to the options set in the WikiContext.
130         */
131        public String getText() {
132            String result;
133    
134            WikiDocument doc = (WikiDocument) getDocument();
135    
136            if (doc == null) {
137                //
138                // This element has not yet been attached anywhere, so we simply assume there is
139                // no rendering and return the plugin name.  This is required e.g. when the 
140                // paragraphify() checks whether the element is empty or not.  We can't of course
141                // know whether the rendering would result in an empty string or not, but let us
142                // assume it does not.
143                //
144    
145                return getPluginName();
146            }
147    
148            WikiContext context = doc.getContext();
149            
150            if( context == null ) {
151                log.info( "WikiContext garbage-collected, cannot proceed" );
152                return getPluginName();
153            }
154    
155            Boolean wysiwygVariable = (Boolean) context.getVariable(RenderingManager.WYSIWYG_EDITOR_MODE);
156            boolean wysiwygEditorMode = false;
157            if (wysiwygVariable != null) {
158                wysiwygEditorMode = wysiwygVariable.booleanValue();
159            }
160    
161            try {
162                //
163                //  Determine whether we should emit the actual code for this plugin or
164                //  whether we should execute it.  For some plugins we always execute it,
165                //  since they can be edited visually.
166                //
167                // FIXME: The plugin name matching should not be done here, but in a per-editor resource
168                if (wysiwygEditorMode && !m_pluginName.matches(EMITTABLE_PLUGINS)) {
169                    result = PLUGIN_START + m_pluginName + SPACE;
170    
171                    // convert newlines to <br> in case the plugin has a body.
172                    String cmdLine = (m_params.get(CMDLINE)).replaceAll(LINEBREAK, ELEMENT_BR);
173    
174                    result = result + cmdLine + PLUGIN_END;
175                } else {
176                    Boolean b = (Boolean) context.getVariable(RenderingManager.VAR_EXECUTE_PLUGINS);
177                    if (b != null && !b.booleanValue()) {
178                        return BLANK;
179                    }
180    
181                    WikiEngine engine = context.getEngine();
182    
183                    Map<String, String> parsedParams = new HashMap<String, String>();
184    
185                    //
186                    //  Parse any variable instances from the string
187                    //
188                    for (Map.Entry<String, String> e : m_params.entrySet()) {
189                        String val = e.getValue();
190                        val = engine.getVariableManager().expandVariables(context, val);
191                        parsedParams.put(e.getKey(), val);
192                    }
193                    PluginManager pm = engine.getPluginManager();
194                    result = pm.execute(context, m_pluginName, parsedParams);
195                }
196            } catch (Exception e) {
197                if (wysiwygEditorMode) {
198                    result = "";
199                } else {
200                    // log.info("Failed to execute plugin",e);
201                    ResourceBundle rb = Preferences.getBundle(context, WikiPlugin.CORE_PLUGINS_RESOURCEBUNDLE);
202                    result = JSPWikiMarkupParser.makeError(
203                            MessageFormat.format(rb.getString("plugin.error.insertionfailed"), e.getMessage())).getText();
204                }
205            }
206    
207    
208            return result;
209        }
210    
211        /**
212         * Executes the executeParse() method.
213         *
214         * @param context The WikiContext
215         * @throws PluginException If something goes wrong.
216         */
217        public void executeParse(WikiContext context) throws PluginException {
218            PluginManager pm = context.getEngine().getPluginManager();
219            if (pm.pluginsEnabled()) {
220                ResourceBundle rb = Preferences.getBundle(context, WikiPlugin.CORE_PLUGINS_RESOURCEBUNDLE);
221                Map<String, String> params = getParameters();
222                WikiPlugin plugin = pm.newWikiPlugin(getPluginName(), rb);
223                try {
224                    if (plugin != null && plugin instanceof ParserStagePlugin) {
225                        ((ParserStagePlugin) plugin).executeParser(this, context, params);
226                    }
227                } catch (ClassCastException e) {
228                    throw new PluginException(MessageFormat.format(rb.getString("plugin.error.notawikiplugin"), getPluginName()), e);
229                }
230            }
231        }
232    
233        /**
234         * Parses a plugin invocation and returns a DOM element.
235         *
236         * @param context     The WikiContext
237         * @param commandline The line to parse
238         * @param pos         The position in the stream parsing.
239         * @return A DOM element
240         * @throws PluginException If plugin invocation is faulty
241         * @since 2.10.0
242         */
243        public static PluginContent parsePluginLine(WikiContext context, String commandline, int pos) throws PluginException {
244            PatternMatcher matcher = new Perl5Matcher();
245    
246            try {
247                PluginManager pm = context.getEngine().getPluginManager();
248                if (matcher.contains(commandline, pm.getPluginPattern())) {
249    
250                    MatchResult res = matcher.getMatch();
251    
252                    String plugin = res.group(2);
253                    String args = commandline.substring(res.endOffset(0),
254                            commandline.length() -
255                                    (commandline.charAt(commandline.length() - 1) == '}' ? 1 : 0));
256                    Map<String, String> arglist = pm.parseArgs(args);
257    
258                    // set wikitext bounds of plugin as '_bounds' parameter, e.g., [345,396]
259                    if (pos != -1) {
260                        int end = pos + commandline.length() + 2;
261                        String bounds = pos + "|" + end;
262                        arglist.put(PluginManager.PARAM_BOUNDS, bounds);
263                    }
264    
265                    PluginContent result = new PluginContent(plugin, arglist);
266    
267                    return result;
268                }
269            } catch (ClassCastException e) {
270                log.error("Invalid type offered in parsing plugin arguments.", e);
271                throw new InternalWikiException("Oops, someone offered !String!");
272            } catch (NoSuchElementException e) {
273                String msg = "Missing parameter in plugin definition: " + commandline;
274                log.warn(msg, e);
275                throw new PluginException(msg);
276            } catch (IOException e) {
277                String msg = "Zyrf.  Problems with parsing arguments: " + commandline;
278                log.warn(msg, e);
279                throw new PluginException(msg);
280            }
281    
282            return null;
283        }
284    
285    }