001    /**
002     * Copyright (c) 2000-2012 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(title.length() - 2, title.length());
101                                    }
102                                    else if (title.startsWith("[")) {
103                                            title = title.substring(title.length() - 1, title.length());
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)
162                    throws WikiException {
163    
164                    LiferayJSPWikiEngine engine = _engines.get(nodeId);
165    
166                    if (engine != null) {
167                            return engine;
168                    }
169    
170                    synchronized (_engines) {
171                            engine = _engines.get(nodeId);
172    
173                            if (engine != null) {
174                                    return engine;
175                            }
176    
177                            Properties nodeProperties = new Properties(_properties);
178    
179                            nodeProperties.setProperty("nodeId", String.valueOf(nodeId));
180    
181                            String appName = nodeProperties.getProperty(
182                                    "jspwiki.applicationName");
183    
184                            nodeProperties.setProperty(
185                                    "jspwiki.applicationName", appName + " for node " + nodeId);
186    
187                            engine = new LiferayJSPWikiEngine(nodeProperties);
188    
189                            _engines.put(nodeId, engine);
190    
191                            return engine;
192                    }
193            }
194    
195            protected synchronized void setProperties(String configuration) {
196                    _properties = new Properties();
197    
198                    InputStream is = new UnsyncByteArrayInputStream(
199                            configuration.getBytes());
200    
201                    try {
202                            _properties.load(is);
203                    }
204                    catch (IOException ioe) {
205                            _log.error(ioe, ioe);
206                    }
207            }
208    
209            private static String _decodeJSPWikiContent(String jspWikiContent) {
210                    return StringUtil.replace(
211                            jspWikiContent, _JSP_WIKI_NAME_2, _JSP_WIKI_NAME_1);
212            }
213    
214            private static String _encodeJSPWikiContent(String content) {
215    
216                    StringBundler encodedContent = new StringBundler();
217    
218                    Matcher commentMatcher = _wikiCommentPattern.matcher(content);
219    
220                    int start = 0;
221                    int end = 0;
222    
223                    String oldContent = StringPool.BLANK;
224                    String newContent = StringPool.BLANK;
225    
226                    while (commentMatcher.find()) {
227                            end = commentMatcher.start();
228    
229                            oldContent = content.substring(start, end);
230    
231                            Matcher wikiLinkMatcher = _wikiLinkPattern.matcher(oldContent);
232    
233                            newContent = oldContent;
234    
235                            while (wikiLinkMatcher.find()) {
236                                    String link = wikiLinkMatcher.group();
237                                    String linkValues = wikiLinkMatcher.group(1);
238    
239                                    String name = linkValues;
240                                    String url = linkValues;
241    
242                                    int pos = linkValues.indexOf(CharPool.PIPE);
243    
244                                    if (pos != -1) {
245                                            name = linkValues.substring(pos + 1, linkValues.length());
246                                            url = linkValues.substring(0, pos);
247                                    }
248    
249                                    String newLink =
250                                            "[[" + _encodeJSPWikiName(url) + "|" + name + "]]";
251    
252                                    newContent = StringUtil.replace(newContent, link, newLink);
253                            }
254    
255                            encodedContent.append(newContent);
256                            encodedContent.append(
257                                    content.substring(
258                                            commentMatcher.start(), commentMatcher.end()));
259    
260                            start = commentMatcher.end();
261                    }
262    
263                    if (start != content.length()) {
264                            encodedContent.append(content.substring(start));
265                    }
266    
267                    return encodedContent.toString();
268            }
269    
270            private static String _encodeJSPWikiName(String name) {
271                    if (name == null) {
272                            return StringPool.BLANK;
273                    }
274    
275                    return StringUtil.replace(name, _JSP_WIKI_NAME_1, _JSP_WIKI_NAME_2);
276            }
277    
278            private static final String[] _JSP_WIKI_NAME_1 = {
279                    StringPool.APOSTROPHE, StringPool.AT, StringPool.CARET,
280                    StringPool.EXCLAMATION, StringPool.INVERTED_EXCLAMATION,
281                    StringPool.INVERTED_QUESTION, StringPool.GRAVE_ACCENT,
282                    StringPool.QUESTION, StringPool.SLASH, StringPool.STAR
283            };
284    
285            private static final String[] _JSP_WIKI_NAME_2 = {
286                    "__APO__", "__AT__", "__CAR__", "__EXM__", "__INE__", "__INQ__",
287                    "__GRA__", "__QUE__", "__SLA__", "__STA__"
288            };
289    
290            private static Log _log = LogFactoryUtil.getLog(JSPWikiEngine.class);
291    
292            private static Pattern _wikiCommentPattern = Pattern.compile(
293                    "[\\{]{3,3}(.*?)[\\}]{3,3}", Pattern.DOTALL);
294            private static Pattern _wikiLinkPattern = Pattern.compile(
295                    "[\\[]{2,2}(.+?)[\\]]{2,2}", Pattern.DOTALL);
296    
297            private Map<Long, LiferayJSPWikiEngine> _engines =
298                    new ConcurrentHashMap<Long, LiferayJSPWikiEngine>();
299            private Properties _properties;
300    
301    }