001    /**
002     * Copyright (c) 2000-present 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.portal.diff;
016    
017    import com.liferay.portal.kernel.diff.DiffHtml;
018    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
019    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
020    import com.liferay.portal.kernel.util.LocaleUtil;
021    import com.liferay.portal.kernel.util.StringPool;
022    import com.liferay.portal.kernel.util.StringUtil;
023    
024    import java.io.Reader;
025    
026    import java.util.Locale;
027    
028    import javax.xml.transform.TransformerFactory;
029    import javax.xml.transform.sax.SAXTransformerFactory;
030    import javax.xml.transform.sax.TransformerHandler;
031    import javax.xml.transform.stream.StreamResult;
032    
033    import org.outerj.daisy.diff.HtmlCleaner;
034    import org.outerj.daisy.diff.XslFilter;
035    import org.outerj.daisy.diff.html.HTMLDiffer;
036    import org.outerj.daisy.diff.html.HtmlSaxDiffOutput;
037    import org.outerj.daisy.diff.html.TextNodeComparator;
038    import org.outerj.daisy.diff.html.dom.DomTreeBuilder;
039    
040    import org.xml.sax.ContentHandler;
041    import org.xml.sax.InputSource;
042    import org.xml.sax.helpers.AttributesImpl;
043    
044    /**
045     * This class can compare two different versions of HTML code. It detects
046     * changes to an entire HTML page such as removal or addition of characters or
047     * images.
048     *
049     * @author Julio Camarero
050     */
051    @DoPrivileged
052    public class DiffHtmlImpl implements DiffHtml {
053    
054            /**
055             * This is a diff method with default values.
056             *
057             * @param  source the source text
058             * @param  target the modified version of the source text
059             * @return a string containing the HTML code of the source text showing the
060             *         differences with the target text
061             * @throws Exception if an exception occurred
062             */
063            @Override
064            public String diff(Reader source, Reader target) throws Exception {
065                    if (source == null) {
066                            throw new NullPointerException("Source is null");
067                    }
068    
069                    if (target == null) {
070                            throw new NullPointerException("Target is null");
071                    }
072    
073                    InputSource oldSource = new InputSource(source);
074                    InputSource newSource = new InputSource(target);
075    
076                    UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
077    
078                    Thread currentThread = Thread.currentThread();
079    
080                    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
081    
082                    currentThread.setContextClassLoader(
083                            DiffHtmlImpl.class.getClassLoader());
084    
085                    try {
086                            SAXTransformerFactory saxTransformerFactory =
087                                    (SAXTransformerFactory)TransformerFactory.newInstance();
088    
089                            TransformerHandler tranformHandler =
090                                    saxTransformerFactory.newTransformerHandler();
091    
092                            tranformHandler.setResult(new StreamResult(unsyncStringWriter));
093    
094                            XslFilter xslFilter = new XslFilter();
095    
096                            ContentHandler contentHandler = xslFilter.xsl(
097                                    tranformHandler,
098                                    "com/liferay/portal/util/dependencies/diff_html.xsl");
099    
100                            HtmlCleaner htmlCleaner = new HtmlCleaner();
101    
102                            DomTreeBuilder oldDomTreeBuilder = new DomTreeBuilder();
103    
104                            htmlCleaner.cleanAndParse(oldSource, oldDomTreeBuilder);
105    
106                            Locale locale = LocaleUtil.getDefault();
107    
108                            TextNodeComparator leftTextNodeComparator = new TextNodeComparator(
109                                    oldDomTreeBuilder, locale);
110    
111                            DomTreeBuilder newDomTreeBuilder = new DomTreeBuilder();
112    
113                            htmlCleaner.cleanAndParse(newSource, newDomTreeBuilder);
114    
115                            TextNodeComparator rightTextNodeComparator = new TextNodeComparator(
116                                    newDomTreeBuilder, locale);
117    
118                            contentHandler.startDocument();
119    
120                            contentHandler.startElement(
121                                    StringPool.BLANK, _DIFF_REPORT, _DIFF_REPORT,
122                                    new AttributesImpl());
123    
124                            contentHandler.startElement(
125                                    StringPool.BLANK, _DIFF, _DIFF, new AttributesImpl());
126    
127                            HtmlSaxDiffOutput htmlSaxDiffOutput = new HtmlSaxDiffOutput(
128                                    contentHandler, _DIFF);
129    
130                            HTMLDiffer htmlDiffer = new HTMLDiffer(htmlSaxDiffOutput);
131    
132                            htmlDiffer.diff(leftTextNodeComparator, rightTextNodeComparator);
133    
134                            contentHandler.endElement(StringPool.BLANK, _DIFF, _DIFF);
135    
136                            contentHandler.endElement(
137                                    StringPool.BLANK, _DIFF_REPORT, _DIFF_REPORT);
138    
139                            contentHandler.endDocument();
140    
141                            unsyncStringWriter.flush();
142    
143                            String string = unsyncStringWriter.toString();
144    
145                            if (string.startsWith("<?xml")) {
146                                    int index = string.indexOf("?>");
147    
148                                    string = string.substring(index + 2);
149                            }
150    
151                            return string;
152                    }
153                    finally {
154                            currentThread.setContextClassLoader(contextClassLoader);
155                    }
156            }
157    
158            @Override
159            public String replaceStyles(String html) {
160                    return StringUtil.replace(
161                            html,
162                            new String[] {
163                                    "changeType=\"diff-added-image\"",
164                                    "changeType=\"diff-changed-image\"",
165                                    "changeType=\"diff-removed-image\"",
166                                    "class=\"diff-html-added\"", "class=\"diff-html-changed\"",
167                                    "class=\"diff-html-removed\""
168                            },
169                            new String[] {
170                                    "style=\"border: 10px solid #CFC;\"",
171                                    "style=\"border: 10px solid blue;\"",
172                                    "style=\"border: 10px solid #FDC6C6;\"",
173                                    "style=\"background-color: #CFC;\"",
174                                    "style=\"border-bottom: 2px dotted blue;\"",
175                                    "style=\"background-color: #FDC6C6; text-decoration: " +
176                                            "line-through;\""
177                            });
178            }
179    
180            private static final String _DIFF = "diff";
181    
182            private static final String _DIFF_REPORT = "diffreport";
183    
184    }