blob: fd85cc35e3dfb90c88d32409aea97739a74fd352 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Michael Scharf (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.internal.terminal.model;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.tm.terminal.model.ITerminalTextData;
import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot;
import org.eclipse.tm.terminal.model.LineSegment;
import org.eclipse.tm.terminal.model.Style;
/**
* This class is thread safe.
*
*/
public class TerminalTextDataStore implements ITerminalTextData {
private char[][] fChars;
private Style[][] fStyle;
private int fWidth;
private int fHeight;
private int fMaxHeight;
private int fCursorColumn;
private int fCursorLine;
public TerminalTextDataStore() {
fChars=new char[0][];
fStyle=new Style[0][];
fWidth=0;
}
/**
* This is used in asserts to throw an {@link RuntimeException}.
* This is useful for tests.
* @return never -- throws an exception
*/
private boolean throwRuntimeException() {
throw new RuntimeException();
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#getWidth()
*/
public int getWidth() {
return fWidth;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#getHeight()
*/
public int getHeight() {
return fHeight;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#setDimensions(int, int)
*/
public void setDimensions(int height, int width) {
assert height>=0 || throwRuntimeException();
assert width>=0 || throwRuntimeException();
// just extend the region
if(height>fChars.length) {
int h=4*height/3;
if(fMaxHeight>0 && h>fMaxHeight)
h=fMaxHeight;
fStyle=(Style[][]) resizeArray(fStyle, height);
fChars=(char[][]) resizeArray(fChars, height);
}
// clean the new lines
if(height>fHeight) {
for (int i = fHeight; i < height; i++) {
fStyle[i]=null;
fChars[i]=null;
}
}
// set dimensions after successful resize!
fWidth=width;
fHeight=height;
}
/**
* Reallocates an array with a new size, and copies the contents of the old
* array to the new array.
*
* @param origArray the old array, to be reallocated.
* @param newSize the new array size.
* @return A new array with the same contents (chopped off if needed or filled with 0 or null).
*/
private Object resizeArray(Object origArray, int newSize) {
int oldSize = Array.getLength(origArray);
if(oldSize==newSize)
return origArray;
Class elementType = origArray.getClass().getComponentType();
Object newArray = Array.newInstance(elementType, newSize);
int preserveLength = Math.min(oldSize, newSize);
if (preserveLength > 0)
System.arraycopy(origArray, 0, newArray, 0, preserveLength);
return newArray;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#getLineSegments(int, int, int)
*/
public LineSegment[] getLineSegments(int line, int column, int len) {
// get the styles and chars for this line
Style[] styles=fStyle[line];
char[] chars=fChars[line];
int col=column;
int n=column+len;
// expand the line if needed....
if(styles==null)
styles=new Style[n];
else if(styles.length<n)
styles=(Style[]) resizeArray(styles, n);
if(chars==null)
chars=new char[n];
else if(chars.length<n)
chars=(char[]) resizeArray(chars, n);
// and create the line segments
Style style=styles[column];
List segments=new ArrayList();
for (int i = column; i < n; i++) {
if(styles[i]!=style) {
segments.add(new LineSegment(col,new String(chars,col,i-col),style));
style=styles[i];
col=i;
}
}
if(col < n) {
segments.add(new LineSegment(col,new String(chars,col,n-col),style));
}
return (LineSegment[]) segments.toArray(new LineSegment[segments.size()]);
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#getChar(int, int)
*/
public char getChar(int line, int column) {
assert column<fWidth || throwRuntimeException();
if(fChars[line]==null||column>=fChars[line].length)
return 0;
return fChars[line][column];
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#getStyle(int, int)
*/
public Style getStyle(int line, int column) {
assert column<fWidth || throwRuntimeException();
if(fStyle[line]==null || column>=fStyle[line].length)
return null;
return fStyle[line][column];
}
void ensureLineLength(int iLine, int length) {
if(length>fWidth)
throw new RuntimeException();
if(fChars[iLine]==null) {
fChars[iLine]=new char[length];
} else if(fChars[iLine].length<length) {
fChars[iLine]=(char[]) resizeArray(fChars[iLine],length);
}
if(fStyle[iLine]==null) {
fStyle[iLine]=new Style[length];
} else if(fStyle[iLine].length<length) {
fStyle[iLine]=(Style[]) resizeArray(fStyle[iLine],length);
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#setChar(int, int, char, org.eclipse.tm.internal.terminal.text.Style)
*/
public void setChar(int line, int column, char c, Style style) {
ensureLineLength(line,column+1);
fChars[line][column]=c;
fStyle[line][column]=style;
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#setChars(int, int, char[], org.eclipse.tm.internal.terminal.text.Style)
*/
public void setChars(int line, int column, char[] chars, Style style) {
setChars(line,column,chars,0,chars.length,style);
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#setChars(int, int, char[], int, int, org.eclipse.tm.internal.terminal.text.Style)
*/
public void setChars(int line, int column, char[] chars, int start, int len, Style style) {
ensureLineLength(line,column+len);
for (int i = 0; i < len; i++) {
fChars[line][column+i]=chars[i+start];
fStyle[line][column+i]=style;
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.internal.terminal.text.ITerminalTextData#scroll(int, int, int)
*/
public void scroll(int startLine, int size, int shift) {
assert startLine+size <= getHeight() || throwRuntimeException();
if(shift<0) {
// move the region up
// shift is negative!!
for (int i = startLine; i < startLine+size+shift; i++) {
fChars[i]=fChars[i-shift];
fStyle[i]=fStyle[i-shift];
}
// then clean the opened lines
cleanLines(Math.max(startLine, startLine+size+shift),Math.min(-shift, getHeight()-startLine));
// cleanLines(Math.max(0, startLine+size+shift),Math.min(-shift, getHeight()-startLine));
} else {
for (int i = startLine+size-1; i >=startLine && i-shift>=0; i--) {
fChars[i]=fChars[i-shift];
fStyle[i]=fStyle[i-shift];
}
cleanLines(startLine, Math.min(shift, getHeight()-startLine));
}
}
/**
* Replaces the lines with new empty data
* @param line
* @param len
*/
private void cleanLines(int line, int len) {
for (int i = line; i < line+len; i++) {
fChars[i]=null;
fStyle[i]=null;
}
}
/*
* @return a text representation of the object.
* Lines are separated by '\n'. No style information is returned.
*/
public String toString() {
StringBuffer buff=new StringBuffer();
for (int line = 0; line < getHeight(); line++) {
if(line>0)
buff.append("\n"); //$NON-NLS-1$
for (int column = 0; column < fWidth; column++) {
buff.append(getChar(line, column));
}
}
return buff.toString();
}
public ITerminalTextDataSnapshot makeSnapshot() {
throw new UnsupportedOperationException();
}
public void addLine() {
if(fMaxHeight>0 && getHeight()<fMaxHeight) {
setDimensions(getHeight()+1, getWidth());
} else {
scroll(0,getHeight(),-1);
}
}
public void copy(ITerminalTextData source) {
fWidth=source.getWidth();
int n=source.getHeight();
if(getHeight()!=n) {
fChars=new char[n][];
fStyle=new Style[n][];
}
for (int i = 0; i < n; i++) {
fChars[i]=source.getChars(i);
fStyle[i]=source.getStyles(i);
}
fHeight=n;
fCursorLine=source.getCursorLine();
fCursorColumn=source.getCursorColumn();
}
public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine,int length) {
for (int i = 0; i < length; i++) {
fChars[i+destStartLine]=source.getChars(i+sourceStartLine);
fStyle[i+destStartLine]=source.getStyles(i+sourceStartLine);
}
}
public void copyLine(ITerminalTextData source, int sourceLine, int destLine) {
fChars[destLine]=source.getChars(sourceLine);
fStyle[destLine]=source.getStyles(sourceLine);
}
public char[] getChars(int line) {
if(fChars[line]==null)
return null;
return (char[]) fChars[line].clone();
}
public Style[] getStyles(int line) {
if(fStyle[line]==null)
return null;
return (Style[]) fStyle[line].clone();
}
public void setLine(int line, char[] chars, Style[] styles) {
fChars[line]=(char[]) chars.clone();
fStyle[line]=(Style[]) styles.clone();
}
public void setMaxHeight(int height) {
fMaxHeight=height;
}
public int getMaxHeight() {
return fMaxHeight;
}
public void cleanLine(int line) {
fChars[line]=null;
fStyle[line]=null;
}
public int getCursorColumn() {
return fCursorColumn;
}
public int getCursorLine() {
return fCursorLine;
}
public void setCursorColumn(int column) {
fCursorColumn=column;
}
public void setCursorLine(int line) {
fCursorLine=line;
}
}