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            @Override
059            public String convert(
060                            com.liferay.portlet.wiki.model.WikiPage page,
061                            PortletURL viewPageURL, PortletURL editPageURL,
062                            String attachmentURLPrefix)
063                    throws PageContentException {
064    
065                    try {
066                            return convert(page);
067                    }
068                    catch (WikiException we) {
069                            throw new PageContentException(we);
070                    }
071            }
072    
073            @Override
074            public Map<String, Boolean> getOutgoingLinks(
075                            com.liferay.portlet.wiki.model.WikiPage page)
076                    throws PageContentException {
077    
078                    if (Validator.isNull(page.getContent())) {
079                            return Collections.emptyMap();
080                    }
081    
082                    try {
083                            LiferayJSPWikiEngine engine = getEngine(page.getNodeId());
084    
085                            WikiPage jspWikiPage = LiferayPageProvider.toJSPWikiPage(
086                                    page, engine);
087    
088                            Collection<String> titles = engine.scanWikiLinks(
089                                    jspWikiPage, _encodeJSPWikiContent(page.getContent()));
090    
091                            Map<String, Boolean> links = new HashMap<String, Boolean>();
092    
093                            for (String title : titles) {
094                                    if (title.startsWith("[[")) {
095                                            title = title.substring(2);
096                                    }
097                                    else if (title.startsWith("[")) {
098                                            title = title.substring(1);
099                                    }
100    
101                                    if (title.endsWith("]]")) {
102                                            title = title.substring(0, title.length() - 2);
103                                    }
104                                    else if (title.endsWith("]")) {
105                                            title = title.substring(0, title.length() - 1);
106                                    }
107    
108                                    Boolean existsObj = links.get(title);
109    
110                                    if (existsObj == null) {
111                                            if (WikiPageLocalServiceUtil.getPagesCount(
112                                                            page.getNodeId(), title, true) > 0) {
113    
114                                                    existsObj = Boolean.TRUE;
115                                            }
116                                            else {
117                                                    existsObj = Boolean.FALSE;
118                                            }
119    
120                                            links.put(title, existsObj);
121                                    }
122                            }
123    
124                            return links;
125                    }
126                    catch (SystemException se) {
127                            throw new PageContentException(se);
128                    }
129                    catch (WikiException we) {
130                            throw new PageContentException(we);
131                    }
132            }
133    
134            @Override
135            public void setInterWikiConfiguration(String interWikiConfiguration) {
136            }
137    
138            @Override
139            public void setMainConfiguration(String mainConfiguration) {
140                    setProperties(mainConfiguration);
141            }
142    
143            @Override
144            public boolean validate(long nodeId, String newContent) {
145                    return true;
146            }
147    
148            protected String convert(com.liferay.portlet.wiki.model.WikiPage page)
149                    throws WikiException {
150    
151                    String content = _encodeJSPWikiContent(page.getContent());
152    
153                    if (Validator.isNull(content)) {
154                            return StringPool.BLANK;
155                    }
156    
157                    com.ecyrd.jspwiki.WikiEngine engine = getEngine(page.getNodeId());
158    
159                    WikiPage jspWikiPage = LiferayPageProvider.toJSPWikiPage(page, engine);
160    
161                    WikiContext wikiContext = new WikiContext(engine, jspWikiPage);
162    
163                    return _decodeJSPWikiContent(engine.textToHTML(wikiContext, content));
164            }
165    
166            protected LiferayJSPWikiEngine getEngine(long nodeId) throws WikiException {
167                    LiferayJSPWikiEngine engine = _engines.get(nodeId);
168    
169                    if (engine != null) {
170                            return engine;
171                    }
172    
173                    synchronized (_engines) {
174                            engine = _engines.get(nodeId);
175    
176                            if (engine != null) {
177                                    return engine;
178                            }
179    
180                            Properties nodeProperties = new Properties(_properties);
181    
182                            nodeProperties.setProperty("nodeId", String.valueOf(nodeId));
183    
184                            String appName = nodeProperties.getProperty(
185                                    "jspwiki.applicationName");
186    
187                            nodeProperties.setProperty(
188                                    "jspwiki.applicationName", appName + " for node " + nodeId);
189    
190                            engine = new LiferayJSPWikiEngine(nodeProperties);
191    
192                            _engines.put(nodeId, engine);
193    
194                            return engine;
195                    }
196            }
197    
198            protected synchronized void setProperties(String configuration) {
199                    _properties = new Properties();
200    
201                    InputStream is = new UnsyncByteArrayInputStream(
202                            configuration.getBytes());
203    
204                    try {
205                            _properties.load(is);
206                    }
207                    catch (IOException ioe) {
208                            _log.error(ioe, ioe);
209                    }
210            }
211    
212            private static String _decodeJSPWikiContent(String jspWikiContent) {
213                    return StringUtil.replace(
214                            jspWikiContent, _JSP_WIKI_NAME_2, _JSP_WIKI_NAME_1);
215            }
216    
217            private static String _encodeJSPWikiContent(String content) {
218                    StringBundler encodedContent = new StringBundler();
219    
220                    Matcher commentMatcher = _wikiCommentPattern.matcher(content);
221    
222                    int start = 0;
223                    int end = 0;
224    
225                    while (commentMatcher.find()) {
226                            end = commentMatcher.start();
227    
228                            String oldContent = content.substring(start, end);
229    
230                            Matcher wikiLinkMatcher = _wikiLinkPattern.matcher(oldContent);
231    
232                            encodedContent.append(_encodeLink(oldContent, wikiLinkMatcher));
233                            encodedContent.append(
234                                    content.substring(
235                                            commentMatcher.start(), commentMatcher.end()));
236    
237                            start = commentMatcher.end();
238                    }
239    
240                    if (start < content.length()) {
241                            content = content.substring(start);
242    
243                            Matcher wikiLinkMatcher = _wikiLinkPattern.matcher(content);
244    
245                            encodedContent.append(_encodeLink(content, wikiLinkMatcher));
246                    }
247    
248                    return encodedContent.toString();
249            }
250    
251            private static String _encodeJSPWikiName(String name) {
252                    if (name == null) {
253                            return StringPool.BLANK;
254                    }
255    
256                    return StringUtil.replace(name, _JSP_WIKI_NAME_1, _JSP_WIKI_NAME_2);
257            }
258    
259            private static String _encodeLink(String content, Matcher wikiLinkMatcher) {
260                    while (wikiLinkMatcher.find()) {
261                            String link = wikiLinkMatcher.group();
262                            String linkValues = wikiLinkMatcher.group(1);
263    
264                            String name = linkValues;
265                            String url = linkValues;
266    
267                            int pos = linkValues.indexOf(CharPool.PIPE);
268    
269                            if (pos != -1) {
270                                    name = linkValues.substring(pos + 1);
271                                    url = linkValues.substring(0, pos);
272                            }
273    
274                            String newLink =
275                                    "[[" + _encodeJSPWikiName(url) + "|" +
276                                            _encodeJSPWikiName(name) + "]]";
277    
278                            content = StringUtil.replace(content, link, newLink);
279                    }
280    
281                    return content;
282            }
283    
284            private static final String[] _JSP_WIKI_NAME_1 = {
285                    StringPool.APOSTROPHE, StringPool.AT, StringPool.CARET,
286                    StringPool.EXCLAMATION, StringPool.INVERTED_EXCLAMATION,
287                    StringPool.INVERTED_QUESTION, StringPool.GRAVE_ACCENT,
288                    StringPool.QUESTION, StringPool.SLASH, StringPool.STAR
289            };
290    
291            private static final String[] _JSP_WIKI_NAME_2 = {
292                    "__APO__", "__AT__", "__CAR__", "__EXM__", "__INE__", "__INQ__",
293                    "__GRA__", "__QUE__", "__SLA__", "__STA__"
294            };
295    
296            private static Log _log = LogFactoryUtil.getLog(JSPWikiEngine.class);
297    
298            private static Pattern _wikiCommentPattern = Pattern.compile(
299                    "[\\{]{3,3}(.*?)[\\}]{3,3}", Pattern.DOTALL);
300            private static Pattern _wikiLinkPattern = Pattern.compile(
301                    "[\\[]{1,2}(.+?)[\\]]{1,2}", Pattern.DOTALL);
302    
303            private Map<Long, LiferayJSPWikiEngine> _engines =
304                    new ConcurrentHashMap<Long, LiferayJSPWikiEngine>();
305            private Properties _properties;
306    
307    }