001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.wiki.engines.jspwiki;
016    
017    import com.ecyrd.jspwiki.WikiContext;
018    import com.ecyrd.jspwiki.WikiException;
019    import com.ecyrd.jspwiki.WikiPage;
020    
021    import com.liferay.portal.kernel.exception.SystemException;
022    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.util.CharPool;
026    import com.liferay.portal.kernel.util.StringBundler;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.kernel.util.StringUtil;
029    import com.liferay.portal.kernel.util.Validator;
030    import com.liferay.portlet.wiki.PageContentException;
031    import com.liferay.portlet.wiki.engines.WikiEngine;
032    import com.liferay.portlet.wiki.service.WikiPageLocalServiceUtil;
033    
034    import java.io.IOException;
035    import java.io.InputStream;
036    
037    import java.util.Collection;
038    import java.util.Collections;
039    import java.util.HashMap;
040    import java.util.Map;
041    import java.util.Properties;
042    import java.util.concurrent.ConcurrentHashMap;
043    import java.util.regex.Matcher;
044    import java.util.regex.Pattern;
045    
046    import javax.portlet.PortletURL;
047    
048    /**
049     * @author Jorge Ferrer
050     */
051    public class JSPWikiEngine implements WikiEngine {
052    
053            public static String decodeJSPWikiName(String jspWikiName) {
054                    return StringUtil.replace(
055                            jspWikiName, _JSP_WIKI_NAME_2, _JSP_WIKI_NAME_1);
056            }
057    
058            public String convert(
059                            com.liferay.portlet.wiki.model.WikiPage page,
060                            PortletURL viewPageURL, PortletURL editPageURL,
061                            String attachmentURLPrefix)
062                    throws PageContentException {
063    
064                    try {
065                            return convert(page);
066                    }
067                    catch (WikiException we) {
068                            throw new PageContentException(we);
069                    }
070            }
071    
072            public Map<String, Boolean> getOutgoingLinks(
073                            com.liferay.portlet.wiki.model.WikiPage page)
074                    throws PageContentException {
075    
076                    if (Validator.isNull(page.getContent())) {
077                            return Collections.emptyMap();
078                    }
079    
080                    try {
081                            LiferayJSPWikiEngine engine = getEngine(page.getNodeId());
082    
083                            WikiPage jspWikiPage = LiferayPageProvider.toJSPWikiPage(
084                                    page, engine);
085    
086                            Collection<String> titles = engine.scanWikiLinks(
087                                    jspWikiPage, _encodeJSPWikiContent(page.getContent()));
088    
089                            Map<String, Boolean> links = new HashMap<String, Boolean>();
090    
091                            for (String title : titles) {
092                                    if (title.startsWith("[[")) {
093                                            title = title.substring(2);
094                                    }
095                                    else if (title.startsWith("[")) {
096                                            title = title.substring(1);
097                                    }
098    
099                                    if (title.endsWith("]]")) {
100                                            title = title.substring(0, title.length() - 2);
101                                    }
102                                    else if (title.endsWith("]")) {
103                                            title = title.substring(0, title.length() - 1);
104                                    }
105    
106                                    Boolean existsObj = links.get(title);
107    
108                                    if (existsObj == null) {
109                                            if (WikiPageLocalServiceUtil.getPagesCount(
110                                                            page.getNodeId(), title, true) > 0) {
111    
112                                                    existsObj = Boolean.TRUE;
113                                            }
114                                            else {
115                                                    existsObj = Boolean.FALSE;
116                                            }
117    
118                                            links.put(title, existsObj);
119                                    }
120                            }
121    
122                            return links;
123                    }
124                    catch (SystemException se) {
125                            throw new PageContentException(se);
126                    }
127                    catch (WikiException we) {
128                            throw new PageContentException(we);
129                    }
130            }
131    
132            public void setInterWikiConfiguration(String interWikiConfiguration) {
133            }
134    
135            public void setMainConfiguration(String mainConfiguration) {
136                    setProperties(mainConfiguration);
137            }
138    
139            public boolean validate(long nodeId, String newContent) {
140                    return true;
141            }
142    
143            protected String convert(com.liferay.portlet.wiki.model.WikiPage page)
144                    throws WikiException {
145    
146                    String content = _encodeJSPWikiContent(page.getContent());
147    
148                    if (Validator.isNull(content)) {
149                            return StringPool.BLANK;
150                    }
151    
152                    com.ecyrd.jspwiki.WikiEngine engine = getEngine(page.getNodeId());
153    
154                    WikiPage jspWikiPage = LiferayPageProvider.toJSPWikiPage(page, engine);
155    
156                    WikiContext wikiContext = new WikiContext(engine, jspWikiPage);
157    
158                    return _decodeJSPWikiContent(engine.textToHTML(wikiContext, content));
159            }
160    
161            protected LiferayJSPWikiEngine getEngine(long nodeId) throws WikiException {
162                    LiferayJSPWikiEngine engine = _engines.get(nodeId);
163    
164                    if (engine != null) {
165                            return engine;
166                    }
167    
168                    synchronized (_engines) {
169                            engine = _engines.get(nodeId);
170    
171                            if (engine != null) {
172                                    return engine;
173                            }
174    
175                            Properties nodeProperties = new Properties(_properties);
176    
177                            nodeProperties.setProperty("nodeId", String.valueOf(nodeId));
178    
179                            String appName = nodeProperties.getProperty(
180                                    "jspwiki.applicationName");
181    
182                            nodeProperties.setProperty(
183                                    "jspwiki.applicationName", appName + " for node " + nodeId);
184    
185                            engine = new LiferayJSPWikiEngine(nodeProperties);
186    
187                            _engines.put(nodeId, engine);
188    
189                            return engine;
190                    }
191            }
192    
193            protected synchronized void setProperties(String configuration) {
194                    _properties = new Properties();
195    
196                    InputStream is = new UnsyncByteArrayInputStream(
197                            configuration.getBytes());
198    
199                    try {
200                            _properties.load(is);
201                    }
202                    catch (IOException ioe) {
203                            _log.error(ioe, ioe);
204                    }
205            }
206    
207            private static String _decodeJSPWikiContent(String jspWikiContent) {
208                    return StringUtil.replace(
209                            jspWikiContent, _JSP_WIKI_NAME_2, _JSP_WIKI_NAME_1);
210            }
211    
212            private static String _encodeJSPWikiContent(String content) {
213                    StringBundler encodedContent = new StringBundler();
214    
215                    Matcher commentMatcher = _wikiCommentPattern.matcher(content);
216    
217                    int start = 0;
218                    int end = 0;
219    
220                    while (commentMatcher.find()) {
221                            end = commentMatcher.start();
222    
223                            String oldContent = content.substring(start, end);
224    
225                            Matcher wikiLinkMatcher = _wikiLinkPattern.matcher(oldContent);
226    
227                            encodedContent.append(_encodeLink(oldContent, wikiLinkMatcher));
228                            encodedContent.append(
229                                    content.substring(
230                                            commentMatcher.start(), commentMatcher.end()));
231    
232                            start = commentMatcher.end();
233                    }
234    
235                    if (start < content.length()) {
236                            content = content.substring(start);
237    
238                            Matcher wikiLinkMatcher = _wikiLinkPattern.matcher(content);
239    
240                            encodedContent.append(_encodeLink(content, wikiLinkMatcher));
241                    }
242    
243                    return encodedContent.toString();
244            }
245    
246            private static String _encodeJSPWikiName(String name) {
247                    if (name == null) {
248                            return StringPool.BLANK;
249                    }
250    
251                    return StringUtil.replace(name, _JSP_WIKI_NAME_1, _JSP_WIKI_NAME_2);
252            }
253    
254            private static String _encodeLink(String content, Matcher wikiLinkMatcher) {
255                    while (wikiLinkMatcher.find()) {
256                            String link = wikiLinkMatcher.group();
257                            String linkValues = wikiLinkMatcher.group(1);
258    
259                            String name = linkValues;
260                            String url = linkValues;
261    
262                            int pos = linkValues.indexOf(CharPool.PIPE);
263    
264                            if (pos != -1) {
265                                    name = linkValues.substring(pos + 1);
266                                    url = linkValues.substring(0, pos);
267                            }
268    
269                            String newLink =
270                                    "[[" + _encodeJSPWikiName(url) + "|" +
271                                            _encodeJSPWikiName(name) + "]]";
272    
273                            content = StringUtil.replace(content, link, newLink);
274                    }
275    
276                    return content;
277            }
278    
279            private static final String[] _JSP_WIKI_NAME_1 = {
280                    StringPool.APOSTROPHE, StringPool.AT, StringPool.CARET,
281                    StringPool.EXCLAMATION, StringPool.INVERTED_EXCLAMATION,
282                    StringPool.INVERTED_QUESTION, StringPool.GRAVE_ACCENT,
283                    StringPool.QUESTION, StringPool.SLASH, StringPool.STAR
284            };
285    
286            private static final String[] _JSP_WIKI_NAME_2 = {
287                    "__APO__", "__AT__", "__CAR__", "__EXM__", "__INE__", "__INQ__",
288                    "__GRA__", "__QUE__", "__SLA__", "__STA__"
289            };
290    
291            private static Log _log = LogFactoryUtil.getLog(JSPWikiEngine.class);
292    
293            private static Pattern _wikiCommentPattern = Pattern.compile(
294                    "[\\{]{3,3}(.*?)[\\}]{3,3}", Pattern.DOTALL);
295            private static Pattern _wikiLinkPattern = Pattern.compile(
296                    "[\\[]{1,2}(.+?)[\\]]{1,2}", Pattern.DOTALL);
297    
298            private Map<Long, LiferayJSPWikiEngine> _engines =
299                    new ConcurrentHashMap<Long, LiferayJSPWikiEngine>();
300            private Properties _properties;
301    
302    }