/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* SourceModification.java
* Creation date: (Feb 17, 2006)
* By: James Wright
*/
package org.openquark.cal.compiler;
import java.util.Comparator;
/**
* A SourceModification models a single modification to a single source file.
* Includes a source position, old text, and new text.
*
* This class is a generalization of the RenameData class, which it replaces.
*
* @author James Wright
* @author Bo Ilic
*/
public abstract class SourceModification {
/** Position to perform replacement at */
private final SourcePosition sourcePosition;
/**
* The corresponding position of sourcePosition in the modified file
*/
private SourcePosition newSourcePosition = null;
/** Maximum size of the summary snippet to print in toString methods */
private static final int MAX_SNIPPET_LENGTH = 32;
/**
* A SourceModification that replaces text at a specific location
* with other text (not necessarily of the same length).
*
* @author James Wright
*/
static final class ReplaceText extends SourceModification {
/** Text to be replaced at sourcePosition */
private final String oldText;
/** Text to replace oldText with at sourcePosition */
private final String newText;
ReplaceText(String oldText, String newText, SourcePosition sourcePosition) {
super(sourcePosition);
if(oldText == null || newText == null) {
throw new NullPointerException();
}
this.oldText = oldText;
this.newText = newText;
}
/** {@inheritDoc} */
@Override
SourceModification getInverse(SourcePosition sourcePosition) {
return new ReplaceText(getNewText(), getOldText(), sourcePosition);
}
/** {@inheritDoc} */
@Override
public String toString() {
StringBuilder outputBuffer = new StringBuilder();
String oldSnippet = getOldText().length() > MAX_SNIPPET_LENGTH ?
getOldText().substring(0, MAX_SNIPPET_LENGTH - 3) + "..." :
getOldText();
String newSnippet = getNewText().length() > MAX_SNIPPET_LENGTH ?
getNewText().substring(0, MAX_SNIPPET_LENGTH - 3) + "..." :
getNewText();
outputBuffer.append("replace '");
outputBuffer.append(oldSnippet);
outputBuffer.append("' with '");
outputBuffer.append(newSnippet);
outputBuffer.append("' at (");
outputBuffer.append(getSourcePosition().getLine());
outputBuffer.append(", ");
outputBuffer.append(getSourcePosition().getColumn());
outputBuffer.append(')');
return outputBuffer.toString();
}
/** {@inheritDoc} */
@Override
String getOldText() {
return oldText;
}
/** {@inheritDoc} */
@Override
String getNewText() {
return newText;
}
}
/**
* A SourceModification that is used to keep track of
* a cursor position.
*
* @author Greg McClement
*/
static final class CursorPosition extends SourceModification {
CursorPosition(SourcePosition sourcePosition) {
super(sourcePosition);
}
/** {@inheritDoc} */
@Override
SourceModification getInverse(SourcePosition sourcePosition) {
return new CursorPosition(sourcePosition);
}
/** {@inheritDoc} */
@Override
public String toString() {
return "";
}
/** {@inheritDoc} */
@Override
String getOldText() {
return "";
}
/** {@inheritDoc} */
@Override
String getNewText() {
return "";
}
}
/**
* A SourceModification that removes specific text at a specific location.
*
* @author James Wright
*/
static final class RemoveText extends SourceModification {
/** Text to be removed at sourcePosition */
private final String oldText;
RemoveText(String oldText, SourcePosition sourcePosition) {
super(sourcePosition);
if(oldText == null) {
throw new NullPointerException();
}
this.oldText = oldText;
}
/** {@inheritDoc} */
@Override
SourceModification getInverse(SourcePosition sourcePosition) {
return new InsertText(getOldText(), sourcePosition);
}
/** {@inheritDoc} */
@Override
public String toString() {
StringBuilder outputBuffer = new StringBuilder();
String removalSnippet = getOldText().length() > MAX_SNIPPET_LENGTH ?
getOldText().substring(0, MAX_SNIPPET_LENGTH - 3) + "..." :
getOldText();
outputBuffer.append("remove '");
outputBuffer.append(removalSnippet);
outputBuffer.append("' at (");
outputBuffer.append(getSourcePosition().getLine());
outputBuffer.append(", ");
outputBuffer.append(getSourcePosition().getColumn());
outputBuffer.append(')');
return outputBuffer.toString();
}
/** {@inheritDoc} */
@Override
String getOldText() {
return oldText;
}
/** {@inheritDoc} */
@Override
String getNewText() {
return "";
}
}
/**
* A SourceModification that inserts some text at a specific location.
*
* @author James Wright
*/
static final class InsertText extends SourceModification {
/** Text to be inserted at sourcePosition */
private final String newText;
InsertText(String newText, SourcePosition sourcePosition) {
super(sourcePosition);
if(newText == null) {
throw new NullPointerException();
}
this.newText = newText;
}
/** {@inheritDoc} */
@Override
SourceModification getInverse(SourcePosition sourcePosition) {
return new RemoveText(getNewText(), sourcePosition);
}
/** {@inheritDoc} */
@Override
public String toString() {
StringBuilder outputBuffer = new StringBuilder();
String importSnippet = getNewText().length() > MAX_SNIPPET_LENGTH ?
getNewText().substring(0, MAX_SNIPPET_LENGTH - 3) + "..." :
getNewText();
outputBuffer.append("insert '");
outputBuffer.append(importSnippet);
outputBuffer.append("' at (");
outputBuffer.append(getSourcePosition().getLine());
outputBuffer.append(", ");
outputBuffer.append(getSourcePosition().getColumn());
outputBuffer.append(')');
return outputBuffer.toString();
}
/** {@inheritDoc} */
@Override
String getOldText() {
return "";
}
/** {@inheritDoc} */
@Override
String getNewText() {
return newText;
}
}
/** Constructor for use by child classes */
private SourceModification(SourcePosition sourcePosition) {
if(sourcePosition == null) {
throw new NullPointerException();
}
this.sourcePosition = sourcePosition;
}
/**
* @return Text that will be removed or replaced by this modification
*/
abstract String getOldText();
/**
* @return Text that will be inserted by this modification
*/
abstract String getNewText();
/** @return SourcePosition in the old source text to perform the modification at */
SourcePosition getSourcePosition() {
return sourcePosition;
}
void setNewSourcePosition(SourcePosition newSourcePosition){
this.newSourcePosition = newSourcePosition;
}
/**
* @return the corresponding position of the original source position in the modified file. This
* is used for example to update old cursor positions to new cursor positions.
*/
SourcePosition getNewSourcePosition(){
return newSourcePosition;
}
/**
* Returns a new SourceModification that performs the opposite of this modification at the
* specified SourcePosition.
* @param positionInModifiedText SourcePosition to perform the inverse modification at
*/
abstract SourceModification getInverse(SourcePosition positionInModifiedText);
/**
* Comparator object to order SourceModification objects by increasing
* source position of the modification.
*/
public static final Comparator<SourceModification> compareByPosition = new CompareByPosition();
private static class CompareByPosition implements Comparator<SourceModification> {
/** {@inheritDoc} */
public int compare(SourceModification o1, SourceModification o2) {
if ((o1 == null) || (o2 == null)) {
throw new NullPointerException();
}
final int compare = SourcePosition.compareByPosition.compare(
o1.getSourcePosition(),
o2.getSourcePosition());
if (compare != 0){
return compare;
}
// if the position matches then cursor position should sort
// last so they pick up the prior modifications
if (o1 instanceof CursorPosition){
if (!(o2 instanceof CursorPosition)){
return 1;
}
}
else{
if (o2 instanceof CursorPosition){
return -1;
}
}
return compare;
}
}
}