/* * HeadsUp Agile * Copyright 2009-2012 Heads Up Development Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.headsupdev.agile.app.files; import org.headsupdev.agile.api.service.Change; import org.headsupdev.agile.api.service.ChangeSet; import org.apache.wicket.model.Model; import org.headsupdev.agile.api.Manager; import java.io.BufferedReader; import java.io.StringReader; import java.io.IOException; import java.io.File; /** * A model that renders the diffs for a given changeset * * @author Andrew Williams * @version $Id$ * @since 1.0 */ public class DiffModel extends Model<String> { private ChangeSet changes; private String match; public DiffModel( ChangeSet changes ) { this( changes, "" ); } public DiffModel( ChangeSet changes, String match ) { this.changes = changes; this.match = match; } public int getHiddenCount() { int others = 0; for ( Change change : changes.getChanges() ) { String fileName = change.getName(); if ( !fileName.startsWith( match ) ) { others++; } } return others; } public String getObject() { StringBuffer diffOut = new StringBuffer(); for ( Change change : changes.getChanges() ) { String fileName = change.getName(); if ( !fileName.startsWith( match ) ) { continue; } String rawDiff = change.getDiff(); if ( rawDiff == null || rawDiff.length() == 0 ) { continue; } String path = fileName.substring( match.length() ); diffOut.append( "<h3><a name=\"" ); diffOut.append( path.replace( File.separatorChar, ':' ) ); diffOut.append( "\"></a>" ); diffOut.append( path ); if ( change.getRevision() != null ) { diffOut.append( "<span wicket:id=\"revision\">" ); diffOut.append( change.getRevision() ); diffOut.append( "</span>" ); } diffOut.append( "</h3>"); markupDiff( rawDiff, diffOut ); } return diffOut.toString(); } public void markupDiff( String in, StringBuffer out ) { if ( in == null ) { return; } out.append( "<table class=\"diff\">" ); BufferedReader reader = new BufferedReader( new StringReader( in ) ) ; try { String line; int newLine = 0, oldLine = 0; boolean displayLines = false; boolean firstCommand = true; while ( ( line = reader.readLine() ) != null ) { boolean displaySpacer = false; char first = '\0'; if ( line.length() > 0) { first = line.charAt( 0 ); } String type; if ( first == '\\' ) { continue; } else if ( first == '@' ) { if ( firstCommand ) { firstCommand = false; } else { displaySpacer = true; } try { String numbers = line.replace( "@", "" ).trim(); String oldRange = numbers.substring( 1, numbers.indexOf( " " ) ); String newRange = numbers.substring( numbers.indexOf( "+" ) + 1 ); if ( oldRange.indexOf( ',' ) != -1 ) { oldLine = Integer.parseInt( oldRange.substring( 0, oldRange.indexOf( ',' ) ) ); } else { oldLine = Integer.parseInt( oldRange ); } if ( newRange.indexOf( ',' ) != -1 ) { newLine = Integer.parseInt( newRange.substring( 0, newRange.indexOf( ',' ) ) ); } else { newLine = Integer.parseInt( newRange ); } if ( !displayLines ) { displayLines = true; continue; } } catch ( NumberFormatException e ) { Manager.getLogger( getClass().getName() ).error( "Failed to parse line numbers from \"" + line + "\"", e ); } } out.append( "<tr>" ); if ( first == '+' ) { if ( displayLines ) { out.append( "<td class=\"linenum-old\"> </td><td class=\"linenum-new\">" ); out.append( newLine++ ); out.append( "</td>" ); } type = "diff-add"; out.append( "<td class=\"sign-add\">+</td>" ); } else if ( first == '-' ) { if ( displayLines ) { out.append( "<td class=\"linenum-old\">" ); out.append( oldLine++ ); out.append( "</td><td class=\"linenum-new\"> </td>" ); } type = "diff-rem"; out.append( "<td class=\"sign-rem\">-</td>" ); } else if ( displaySpacer ) { if ( displayLines ) { out.append( "<td class=\"linenum-old\"> </td><td class=\"linenum-new\"> </td>" ); } type = "diff-skip"; line = "...."; out.append( "<td class=\"sign-none\"> </td>" ); } else { if ( displayLines ) { out.append( "<td class=\"linenum-old\">" ); out.append( oldLine++ ); out.append( "</td><td class=\"linenum-new\">" ); out.append( newLine++ ); out.append( "</td>" ); } type = "diff-line"; out.append( "<td class=\"sign-none\"> </td>" ); } out.append( "<td class=\"" ); out.append( type ); out.append( "\">" ); out.append( encode( line.substring( 1 ) ) ); out.append( "</td></tr>" ); } } catch ( IOException e ) { /* really not gonna happen */ } out.append( "</table>" ); } public String encode( String in ) { if ( in.length() == 0 ) { return " "; } StringBuilder ret = new StringBuilder( in.length() ); for ( int i = 0; i < in.length(); i++ ) { char chr = in.charAt( i ); switch (chr) { case '>': ret.append( ">" ); break; case '<': ret.append( "<" ); break; case '\"': ret.append( """ ); break; case ' ': ret.append( " " ); break; case '&': ret.append( "&" ); break; case '\t': ret.append( "    " ); break; default: ret.append( chr ); } } return ret.toString(); } }