/**
* $Id: $
* $Date: $
*
*/
package org.xmlsh.sh.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import org.xmlsh.util.CharAttr;
import lombok.val;
/*
* Like StringBuilder but stores extended attributes with each charactor.
*
*/
public class CharAttributeBuffer {
private static final int DEFAULT_CAPACITY = 256;
private int capacity;
private int length;
private char[] charArray;
private byte[] attrArray;
@FunctionalInterface
public interface CharAttributeDecoder {
void decode(StringBuilder sb, char ch, char esc, byte attrs);
}
@FunctionalInterface
public interface CharAttributeEncoder {
byte encode(CharAttributeBuffer sb, char ch, char esc , byte attrs);
}
public CharAttributeBuffer() {
this(DEFAULT_CAPACITY);
}
public CharAttributeBuffer(int capacity) {
this.capacity = capacity;
charArray = new char[capacity];
attrArray = new byte[capacity];
}
public CharAttributeBuffer clone() {
return new CharAttributeBuffer(this);
}
public CharAttributeBuffer(CharAttributeBuffer that) {
this(that == null ? 0 : that.capacity);
if(that != null)
append(that);
}
public CharAttributeBuffer(String s, CharAttr attr) {
this(s, (byte) attr.toBit());
}
public CharAttributeBuffer(String s, CharAttrs attrs) {
this(s, (byte) attrs.toBits());
}
public CharAttributeBuffer(String s, EnumSet<CharAttr> attrs) {
this(s, (byte) CharAttrs.toBits(attrs));
}
public CharAttributeBuffer(String s, byte attr) {
int size = s.length();
capacity = size + DEFAULT_CAPACITY - (size % DEFAULT_CAPACITY);
charArray = new char[capacity];
attrArray = new byte[capacity];
append(s, attr);
}
private CharAttributeBuffer(char[] ca, byte[] aa, int len, int c) {
charArray = ca;
attrArray = aa;
length = len;
capacity = c;
}
public CharAttributeBuffer(String s) {
this(s, (byte) 0);
}
public CharAttributeBuffer append(char c) {
return append(c, (byte) 0);
}
public CharAttributeBuffer append(char c, CharAttrs attr) {
return append(c, attr.toBits());
}
public CharAttributeBuffer append(char c, byte attr) {
ensure(length + 1);
charArray[length] = c;
attrArray[length++] = attr;
return this;
}
private void ensure(int size) {
if(size <= capacity)
return;
capacity = size + DEFAULT_CAPACITY - (size % DEFAULT_CAPACITY);
charArray = Arrays.copyOf(charArray, capacity);
attrArray = Arrays.copyOf(attrArray, capacity);
}
public int attrAt(int pos) {
return attrArray[pos];
}
public char charAt(int pos) {
return charArray[pos];
}
@Override
public String toString() {
return decodeString();
}
public static CharAttributeBuffer encodeString(String s, CharAttributeEncoder encoder, char esc, CharAttrs attrs ) {
if( s == null ) return null ; // preserve null
byte ca = attrs.toBits();
CharAttributeBuffer b = new CharAttributeBuffer(s.length());
for( char c : s.toCharArray() )
ca = encoder.encode( b , c , esc , ca);
return b;
}
public static String decodeString(CharAttributeBuffer cb , CharAttributeDecoder decoder, char esc ) {
if( cb == null ) return null;
StringBuilder sb = new StringBuilder(cb.length);
for(int i = 0; i < cb.length; i++)
decoder.decode(sb, cb.charArray[i], esc , cb.attrArray[i]);
return sb.toString();
}
public static CharAttributeDecoder defaultDecoder =
(StringBuilder sb, char ch, char esc , byte attrs) -> {
if((attrs & CharAttr.ATTR_ESCAPED.toBit()) != 0)
sb.append(esc);
sb.append(ch);
} ;
public static CharAttributeEncoder defaultEncoder =
(CharAttributeBuffer cb, char ch, char esc, byte attrs) -> {
if( attrs == 0 ){ // not escaped
if( ch == esc)
return CharAttr.ATTR_ESCAPED.toBit();
}
cb.append(ch,attrs);
return 0;
} ;
public static String decodeString( CharAttributeBuffer sb, char esc) {
return decodeString(sb , defaultDecoder, esc);
}
public String decodeString() {
return decodeString( this , '\\');
}
public String decodeString(char esc) {
return decodeString( this , esc);
}
public static CharAttributeBuffer encodeString(String s, char esc, CharAttrs attrs) {
return encodeString( s,defaultEncoder, esc, attrs);
}
public static CharAttributeBuffer encodeString(String s, char esc) {
return encodeString(s,esc,CharAttrs.NONE);
}
public static CharAttributeBuffer encodeString(String s) {
return encodeString( s,defaultEncoder, '\\',CharAttrs.NONE);
}
public char[] getCharArray() {
return Arrays.copyOf(charArray, length);
}
public byte[] getAttrArray() {
return Arrays.copyOf(attrArray, length);
}
public int size() {
return length;
}
public boolean isEmpty() {
return length == 0;
}
public void clear() {
length = 0;
}
public void append(String s) {
append(s, (byte) 0);
}
public void append(String s, byte attr) {
int len = s.length();
ensure(length + len);
s.getChars(0, len, charArray, length);
Arrays.fill(attrArray, length, length + len, attr);
length += len;
}
public int indexOf(int start, char c, byte attr) {
for(int i = start; i < length; i++) {
if(charArray[i] == c && attrArray[i] == attr)
return i;
}
return -1;
}
public int indexOf(char delim) {
return indexOf( 0,delim , CharAttr.NONE );
}
public int lastIndexOf(char c) {
return lastIndexOf( 0 , c, CharAttr.NONE );
}
public int lastIndexOf(int first , char c, byte attr) {
for(int i = length-1; i >=first ; i--) {
if(charArray[i] == c && attrArray[i] == attr)
return i;
}
return -1;
}
public void delete(int start, int len) {
if(start + len > length)
len = (length - start);
if(len <= 0)
return;
System.arraycopy(charArray, start + len, charArray, start, length - len);
System.arraycopy(attrArray, start + len, attrArray, start, length - len);
length -= len;
}
// split using c as an optionally repeatable seperator e.g. a/b//c == [a,b,c]
public CharAttributeBuffer[] split(char c,boolean repeatable) {
ArrayList<CharAttributeBuffer> list = new ArrayList<CharAttributeBuffer>();
int start = 0;
for(int i = 0; i < length; i++) {
if(charArray[i] == c) {
list.add(subsequence(start, i));
if( repeatable)
while(i < length && charArray[i + 1] == c)
i++;
start = i + 1;
}
}
if(start < length)
list.add(subsequence(start, length));
return list.toArray(new CharAttributeBuffer[list.size()]);
}
public CharAttributeBuffer subsequence(int start) {
return subsequence(start,length);
}
public CharAttributeBuffer subsequence(int start, int i) {
return new CharAttributeBuffer(
Arrays.copyOfRange(charArray, start, i),
Arrays.copyOfRange(attrArray, start, i),
i - start,
capacity);
}
@Override
public boolean equals(Object that) {
if(this == that)
return true;
if(that instanceof CharAttributeBuffer) {
CharAttributeBuffer cb = (CharAttributeBuffer) that;
if(length == cb.length) {
for(int i = 0; i < length; i++) {
if(charArray[i] != cb.charArray[i] ||
attrArray[i] != cb.attrArray[i])
return false;
}
return true;
}
}
return false;
}
public boolean stringEquals(String s) {
int len = s.length();
if(length != len)
return false;
for(int i = 0; i < len; i++)
if(charArray[i] != s.charAt(i))
return false;
return true;
}
public CharAttributeBuffer append(CharAttributeBuffer achars) {
if(achars != null) {
int len = achars.length;
ensure(length + len);
System.arraycopy(achars.charArray, 0, charArray, 0, len);
System.arraycopy(achars.attrArray, 0, attrArray, 0, len);
length += len;
}
return this ;
}
public void append(String s, CharAttrs attr) {
append(s, attr.toBits());
}
public void logString(StringBuilder sb) {
sb.append("[");
for(int i = 0; i < 10 && i < length; i++) {
if(i > 0)
sb.append(',');
sb.append(charArray[i]).append(':')
.append(CharAttrs.fromBits(attrArray[i]));
}
}
public String[] splitString(char cs) {
return splitString(cs,'\\',false);
}
public String[] splitString(char cs,char esc, boolean repeatable) {
val cv = split(cs,repeatable);
val sa = new String[cv.length];
int i =0 ;
for( val v : cv ){
sa[i++] = v.decodeString(esc) ;
}
return sa;
}
}
/*
* Copyright (C) 2008-2012 David A. Lee.
*
* The contents of this file are subject to the "Simplified BSD License" (the
* "License");
* you may not use this file except in compliance with the License. You may
* obtain a copy of the
* License at http://www.opensource.org/licenses/bsd-license.php
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is: all this file.
*
* The Initial Developer of the Original Code is David A. Lee
*
* Portions created by (your name) are Copyright (C) (your legal entity). All
* Rights Reserved.
*
* Contributor(s): David A. Lee
*
*/