001/*-----------------------------------------------------------------------+ 002 | com.teamscale.ui 003 | | 004 $Id$ 005 | | 006 | Copyright (c) 2009-2013 CQSE GmbH | 007 +-----------------------------------------------------------------------*/ 008package org.conqat.lib.commons.diff; 009 010import java.util.ArrayList; 011import java.util.List; 012import java.util.regex.Matcher; 013import java.util.regex.Pattern; 014 015import org.conqat.lib.commons.region.Region; 016import org.conqat.lib.commons.string.LineOffsetConverter; 017 018/** 019 * Class for calculating diff based on given regions. 020 */ 021public class RegionDiffer extends DifferBase<String> { 022 023 /** 024 * Pattern used to parse region. See {@link #RegionDiffer(String)} for 025 * details. 026 */ 027 private static final Pattern REGION_PATTERN = Pattern.compile("(\\d+)-(\\d+):(\\d+)-(\\d+)"); 028 029 /** The region in the left element. */ 030 private final Region leftRegion; 031 032 /** The region in the right element. */ 033 private final Region rightRegion; 034 035 /** 036 * Constructor. 037 * 038 * @param regionDescription 039 * description of the regions as a string, formatted as 040 * "leftStart-leftEnd:rightStart-rightEnd", where all are 041 * one-based inclusive line numbers. 042 * @throws IllegalArgumentException 043 * if the regionDescription does not follow the required format. 044 */ 045 public RegionDiffer(String regionDescription) throws IllegalArgumentException { 046 Matcher matcher = REGION_PATTERN.matcher(regionDescription); 047 if (!matcher.matches()) { 048 throw new IllegalArgumentException("Invalid region description: " + regionDescription); 049 } 050 051 try { 052 leftRegion = new Region(Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2))); 053 rightRegion = new Region(Integer.parseInt(matcher.group(3)), Integer.parseInt(matcher.group(4))); 054 } catch (NumberFormatException e) { 055 throw new IllegalArgumentException("Invalid region description: " + regionDescription); 056 } 057 } 058 059 /** {@inheritDoc} */ 060 @Override 061 protected String getElementText(String element) { 062 return element; 063 } 064 065 /** {@inheritDoc} */ 066 @Override 067 protected String getDiffName() { 068 return "region-based"; 069 } 070 071 /** 072 * This method return only three chunks. One for the text before the given 073 * region, the given region, and the text after. The comparison text is 074 * chosen such that the diff will be in the given region. 075 */ 076 @Override 077 protected List<TextChunk> getChunks(String elementText, boolean isLeft) { 078 079 Region region = leftRegion; 080 if (!isLeft) { 081 region = rightRegion; 082 } 083 084 LineOffsetConverter converter = new LineOffsetConverter(elementText); 085 086 List<TextChunk> result = new ArrayList<TextChunk>(); 087 088 // both pre-regions get the same comparison text, hence are mapped to 089 // each other 090 result.add(new TextChunk(0, converter.getOffset(region.getStart()), 1, region.getStart(), "pre")); 091 092 // both main regions get different comparison text, hence are detected 093 // as change 094 result.add(new TextChunk(converter.getOffset(region.getStart()), converter.getOffset(region.getEnd() + 1), 095 region.getStart(), region.getEnd() + 1, "content" + isLeft)); 096 097 // both post-regions get the same comparison text, hence are mapped to 098 // each other 099 result.add(new TextChunk(converter.getOffset(region.getEnd() + 1), elementText.length(), region.getEnd() + 1, 100 converter.getLine(elementText.length()) + 1, "post")); 101 return result; 102 } 103}