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 020package org.apache.wiki.diff; 021 022import org.apache.log4j.Logger; 023import org.apache.wiki.WikiContext; 024import org.apache.wiki.WikiEngine; 025import org.apache.wiki.api.exceptions.NoRequiredPropertyException; 026import org.apache.wiki.util.FileUtil; 027import org.apache.wiki.util.TextUtil; 028 029import java.io.BufferedReader; 030import java.io.File; 031import java.io.IOException; 032import java.io.StringReader; 033import java.nio.charset.Charset; 034import java.nio.charset.StandardCharsets; 035import java.util.Properties; 036 037/** 038 * This DiffProvider allows external command line tools to be used to generate 039 * the diff. 040 */ 041public class ExternalDiffProvider implements DiffProvider 042{ 043 private static final Logger log = Logger.getLogger(ExternalDiffProvider.class); 044 045 /** 046 * Determines the command to be used for 'diff'. This program must be able 047 * to output diffs in the unified format. For example 'diff -u %s1 %s2'. 048 */ 049 public static final String PROP_DIFFCOMMAND = "jspwiki.diffCommand"; 050 051 private String m_diffCommand = null; 052 private Charset m_encoding; 053 054 private static final char DIFF_ADDED_SYMBOL = '+'; 055 private static final char DIFF_REMOVED_SYMBOL = '-'; 056 057 private static final String CSS_DIFF_ADDED = "<tr><td bgcolor=\"#99FF99\" class=\"diffadd\">"; 058 private static final String CSS_DIFF_REMOVED = "<tr><td bgcolor=\"#FF9933\" class=\"diffrem\">"; 059 private static final String CSS_DIFF_UNCHANGED = "<tr><td class=\"diff\">"; 060 private static final String CSS_DIFF_CLOSE = "</td></tr>"; 061 062 //FIXME This could/should be a property for this provider, there is not guarentee that 063 //the external program generates a format suitible for the colorization code of the 064 //TraditionalDiffProvider, currently set to true for legacy compatibility. 065 //I don't think this 'feature' ever worked right, did it?... 066 private boolean m_traditionalColorization = true; 067 068 /** 069 * Creates a new ExternalDiffProvider. 070 */ 071 public ExternalDiffProvider() 072 { 073 } 074 075 /** 076 * @see org.apache.wiki.WikiProvider#getProviderInfo() 077 * {@inheritDoc} 078 */ 079 public String getProviderInfo() 080 { 081 return "ExternalDiffProvider"; 082 } 083 084 /** 085 * {@inheritDoc} 086 * @see org.apache.wiki.WikiProvider#initialize(org.apache.wiki.WikiEngine, java.util.Properties) 087 */ 088 public void initialize( WikiEngine engine, Properties properties ) 089 throws NoRequiredPropertyException, IOException 090 { 091 m_diffCommand = properties.getProperty( PROP_DIFFCOMMAND ); 092 093 if( (null == m_diffCommand) || (m_diffCommand.trim().equals( "" )) ) 094 { 095 throw new NoRequiredPropertyException( "ExternalDiffProvider missing required property", PROP_DIFFCOMMAND ); 096 } 097 098 m_encoding = engine.getContentEncoding(); 099 } 100 101 102 /** 103 * Makes the diff by calling "diff" program. 104 * {@inheritDoc} 105 */ 106 public String makeDiffHtml( WikiContext ctx, String p1, String p2 ) 107 { 108 File f1 = null; 109 File f2 = null; 110 String diff = null; 111 112 try 113 { 114 f1 = FileUtil.newTmpFile(p1, m_encoding); 115 f2 = FileUtil.newTmpFile(p2, m_encoding); 116 117 String cmd = TextUtil.replaceString(m_diffCommand, "%s1", f1.getPath()); 118 cmd = TextUtil.replaceString(cmd, "%s2", f2.getPath()); 119 120 String output = FileUtil.runSimpleCommand(cmd, f1.getParent()); 121 122 // FIXME: Should this rely on the system default encoding? 123 String rawWikiDiff = new String( output.getBytes( StandardCharsets.ISO_8859_1 ), m_encoding ); 124 125 String htmlWikiDiff = TextUtil.replaceEntities( rawWikiDiff ); 126 127 if (m_traditionalColorization) //FIXME, see comment near declaration... 128 diff = colorizeDiff(htmlWikiDiff); 129 else 130 diff = htmlWikiDiff; 131 132 } catch (IOException e) { 133 log.error("Failed to do file diff", e); 134 } catch (InterruptedException e) { 135 log.error("Interrupted", e); 136 } finally { 137 if( f1 != null ) { 138 f1.delete(); 139 } 140 if( f2 != null ) { 141 f2.delete(); 142 } 143 } 144 145 return diff; 146 } 147 148 149 /** 150 * Goes through output provided by a diff command and inserts HTML tags to 151 * make the result more legible. Currently colors lines starting with a + 152 * green, those starting with - reddish (hm, got to think of color blindness 153 * here...). 154 */ 155 static String colorizeDiff(String diffText) throws IOException { 156 if( diffText == null ) { 157 return "Invalid diff - probably something wrong with server setup."; 158 } 159 160 String line = null; 161 String start = null; 162 String stop = null; 163 164 BufferedReader in = new BufferedReader( new StringReader( diffText ) ); 165 StringBuilder out = new StringBuilder(); 166 167 out.append("<table class=\"diff\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"); 168 while( (line = in.readLine()) != null ) 169 { 170 stop = CSS_DIFF_CLOSE; 171 172 if( line.length() > 0 ) 173 { 174 switch( line.charAt( 0 ) ) 175 { 176 case DIFF_ADDED_SYMBOL: 177 start = CSS_DIFF_ADDED; 178 break; 179 case DIFF_REMOVED_SYMBOL: 180 start = CSS_DIFF_REMOVED; 181 break; 182 default: 183 start = CSS_DIFF_UNCHANGED; 184 } 185 } 186 else 187 { 188 start = CSS_DIFF_UNCHANGED; 189 } 190 191 out.append( start ); 192 out.append( line.trim() ); 193 out.append( stop + "\n" ); 194 } 195 196 out.append( "</table>\n" ); 197 return out.toString(); 198 } 199}