org.jclarion.clarion.ClarionString Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of clarion-runtime Show documentation
Show all versions of clarion-runtime Show documentation
JClarion runtime environment
/**
* Copyright 2010, by Andrew Barnham
*
* The contents of this file are subject to
* GNU Lesser General Public License (LGPL), v.3
* http://www.gnu.org/licenses/lgpl.txt
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
*/
package org.jclarion.clarion;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jclarion.clarion.constants.Match;
import org.jclarion.clarion.lang.Constant;
import org.jclarion.clarion.lang.StringEncoding;
import org.jclarion.clarion.memory.CMem;
import org.jclarion.clarion.primative.AbstractStateFactory;
import org.jclarion.clarion.primative.AbstractStateGetter;
import org.jclarion.clarion.primative.GlobalStateGetter;
import org.jclarion.clarion.primative.ThreadStateGetter;
import org.jclarion.clarion.runtime.format.Formatter;
/**
* Model a string in clarion
*
* Internal storage of string uses following parameters:
*
* lState.string - String
* lState.chararray - chararray
* lState.chararray_len - string len in char array
*
* lState.chararry is the accurate representation. and lState.string is non null
* when requested and chararray has not subsequently mutated
*
* it is possible for string to be defined by chararray not to be defined
* when first mutation is needed - chararray is retrieved from the string
*
*
* @author barney
*
*/
public class ClarionString extends ClarionObject
{
public static final int STRING=1;
public static final int CSTRING=2;
public static final int PSTRING=3;
public static final int ASTRING=4;
private boolean contentConstructed; // true if object was constructed via content
private int encoding=STRING;
private int size=-1;
public static class State
{
private String string;
private char[] chararray;
private int len;
public State(ClarionString.State base) {
string=base.string;
len=base.len;
if (base.chararray!=null) chararray=base.chararray.clone();
}
public State() {
}
}
private static class StateFactory extends AbstractStateFactory
{
@Override
public State cloneState(State base) {
return new State(base);
}
@Override
public State createState() {
return new State();
}
}
private static StateFactory factory=new StateFactory();
private AbstractStateGetter state=new GlobalStateGetter(factory);
public void initThread()
{
if (!state.isThreaded()) state=new ThreadStateGetter(factory,state);
super.initThread();
}
public ClarionString setThread()
{
initThread();
return this;
}
@Override
public Object getLockedObject(Thread t)
{
if (!state.isThreaded()) return this;
return new ClarionString(this,t);
}
public ClarionString(ClarionString base,Thread lock)
{
super(base,lock);
this.encoding=base.encoding;
this.contentConstructed=base.contentConstructed;
this.size=base.size;
if (lock!=null) this.state=base.state.getLockedGetter(lock);
}
/**
* Create undefined string
*/
public ClarionString()
{
}
/**
* Create empty string of specified size. space padded.
*/
public ClarionString(int size)
{
this.size=size;
clear();
}
/**
* Create string of given size defaulted to specified content.
*/
public ClarionString(String content)
{
State lState = state.get();
this.size=content.length();
this.contentConstructed=true;
lState.string=content;
}
/**
* Create string of given size defaulted to specified content.
*/
public ClarionString(char content[])
{
State lState = state.get();
this.size=content.length;
lState.chararray=content;
lState.len=content.length;
}
public String toString()
{
State lState = state.get();
if (lState.string==null) {
if (lState.chararray!=null) {
lState.string=new String(lState.chararray,0,lState.len);
} else if (size==-1) {
lState.string="";
}
}
return lState.string;
}
/**
* clone string
*
* @return
*/
public ClarionString like()
{
ClarionString cs = new ClarionString();
cs.encoding=this.encoding;
cs.size=this.size;
cs.state.get().string=this.toString();
return cs;
}
private void getChars(int begin,int end,char dest[],int d_ofs)
{
State lState = state.get();
if (lState.chararray!=null) {
System.arraycopy(lState.chararray,begin,dest,d_ofs,end-begin);
return;
}
if (lState.string!=null) {
lState.string.getChars(begin,end, dest,d_ofs);
return;
}
}
private char charAt(int ofs)
{
State lState = state.get();
if (lState.chararray!=null) {
if (ofs<0 || ofs>=lState.len) return 0;
return lState.chararray[ofs];
}
if (lState.string!=null) {
if (ofs<0 || ofs>=lState.string.length()) return 0;
return lState.string.charAt(ofs);
}
return 0;
}
private int getLength()
{
State lState = state.get();
if (lState.chararray!=null) {
return lState.len;
}
if (lState.string!=null) {
return lState.string.length();
}
return 0;
}
private boolean getCharArray(int len,boolean cloneIfTooSmall)
{
State lState = state.get();
int size=allowedSize();
if (lState.chararray==null) {
if (lState.string==null) {
lState.chararray=new char[size>len?size:len];
lState.len=0;
} else {
lState.chararray=lState.string.toCharArray();
lState.len=lState.chararray.length;
}
}
if (lState.chararray.lengthlen?size:len];
if (cloneIfTooSmall) {
System.arraycopy(lState.chararray,0,t,0,lState.len);
} else {
lState.len=0;
}
lState.chararray=t;
lState.string=null;
return true;
}
return false;
}
private boolean writeAndReportChange(ClarionString source,int len)
{
return writeAndReportChange(0,source,len);
}
private boolean writeAndReportChange(int ofs,ClarionString source,int len)
{
State lState = state.get();
boolean change=getCharArray(ofs+len,false);
if (!change && !isAnyoneInterestedInChange()) change=true;
lState.len=ofs+len;
if (!change) {
while (len>0) {
len--;
char c = source.charAt(len);
if (c!=lState.chararray[ofs+len]) {
change=true;
lState.chararray[ofs+len]=c;
break;
}
lState.chararray[ofs+len]=c;
}
}
if (len>0) {
source.getChars(0,len,lState.chararray,ofs);
}
return change;
}
@Override
public void setValue(ClarionObject object) {
State lState = state.get();
ClarionString st = null;
if (object!=null) {
st = object.getString();
} else {
st=new ClarionString();
}
int allowedSize=allowedSize();
if (allowedSize==-1) {
// when size of recipient string is not restricted
int o_len = getLength();
if (writeAndReportChange(st,st.getLength())) {
lState.string=null;
notifyChange();
} else {
if (lState.string!=null && lState.len!=lState.string.length()) {
lState.string=null;
notifyChange();
} else if (o_len != getLength()) {
notifyChange();
}
}
return;
}
if (encoding==STRING) {
getCharArray(0,false);
if (st.getLength()allowedSize) {
if (writeAndReportChange(st,allowedSize)) {
lState.string=null;
notifyChange();
}
return;
} else {
boolean lengthChange = encoding!=STRING && getLength()!=st.getLength();
if (writeAndReportChange(st,st.getLength())) {
lState.string=null;
notifyChange();
}
if (lengthChange) {
lState.string=null;
notifyChange();
}
return;
}
}
@Override
public ClarionObject add(ClarionObject object) {
return toNumber().add(object);
}
@Override
public int compareTo(ClarionObject object) {
object=object.getValue();
if (object instanceof ClarionNumber || object instanceof ClarionDecimal || object instanceof ClarionReal || object instanceof ClarionBool)
{
boolean numeric=true;
for (int scan=getLength()-1;scan>=0;scan--) {
char c=charAt(scan);
if (c!=' ' && c!=0) {
numeric=false;
}
}
if (!numeric) numeric=isNumeric(true,false);
if (numeric) {
if (isDecimal()) {
return getDecimal().compareTo(object);
} else {
return getNumber().compareTo(object);
}
}
}
ClarionString r = object.getString();
int l1 = getLength();
int l2 = r.getLength();
while (l1>0) {
char f = charAt(l1-1);
if (f!=' ') break;
l1--;
}
while (l2>0) {
char f = r.charAt(l2-1);
if (f!=' ') break;
l2--;
}
int min = l1;
if (l20) return 1;
}
if (l1l2) return 1;
return 0;
}
@Override
public int intValue() {
if (isNumeric(false)) {
try {
return Integer.parseInt(toString().trim());
} catch (NumberFormatException ex) {
return 0;
}
}
if (isNumeric(true)) {
String bit = toString();
int dot = bit.indexOf('.');
if (dot==0) return 0;
try {
return Integer.parseInt(bit.substring(0,dot).trim());
} catch (NumberFormatException ex) {
return 0;
}
}
return 0;
}
@Override
public ClarionObject multiply(ClarionObject object)
{
return toNumber().multiply(object);
}
/**
* Set external name for the object - used by external systems like SQL driver
* @param name
* @return
*/
public ClarionString setName(String name)
{
doSetName(name);
return this;
}
/**
* return 1d array clone of the object based on size. Note java object
* array returned is one larger than specified as clarion starts arrays at
* offset '1' whilst java as offset '0'
*/
public ClarionArray dim(int size)
{
return new ClarionArray(this,size);
}
/**
* Set string encoding. A few options exists
*
* String - fixed width
* pstring - fixed width memory footprint but first char specifies string length
* cstring - fixed width memory footprint - 0x00 terminated string
* astring - no idea
*
* @param encoding
* @return
*/
public ClarionString setEncoding(int encoding)
{
State lState = state.get();
if (contentConstructed) {
size++; // resize in memory
} else {
if (encoding!=STRING) {
lState.string=null;
lState.chararray=new char[0];
lState.len=0;
}
}
this.encoding=encoding;
return this;
}
/**
* Overlay string onto another clarion object
*
* @param o
* @return
*/
public ClarionString setOver(ClarionMemoryModel o)
{
doSetOver(o);
return this;
}
@Override
public ClarionObject divide(ClarionObject object) {
return toNumber().divide(object);
}
@Override
public ClarionBool getBool() {
return new ClarionBool(boolValue());
}
@Override
public ClarionNumber getNumber() {
return new ClarionNumber(intValue());
}
@Override
public ClarionReal getReal() {
return new ClarionReal(toString().trim());
}
@Override
public ClarionString getString() {
return this;
}
@Override
public ClarionObject modulus(ClarionObject object) {
return toNumber().modulus(object);
}
@Override
public ClarionObject power(ClarionObject object) {
return toNumber().power(object);
}
@Override
public ClarionObject subtract(ClarionObject object) {
return toNumber().subtract(object);
}
@Override
public boolean boolValue() {
for (int scan=0;scan0) {
if (encoding==STRING) {
fill='\u00ff';
} else {
if (size==-1) {
size=1;
}
fill='\u00ff';
}
}
if (zero || size<=0) {
setValue("");
} else {
char nstring[]=new char[size];
for (int i=0;it) throw new IllegalArgumentException("Index invalid");
if (t>size && size>=0) throw new IllegalArgumentException("Index invalid");
ClarionString s = string.getString();
int size = t-f+1;
f--;
t--;
boolean change = getCharArray(t+1,true);
while (lState.len<=t) {
lState.chararray[lState.len++]=' ';
}
int l = lState.len;
if (size>s.getLength()) {
if (writeAndReportChange(f,s,s.getLength())) change=true;
size-=s.getLength();
f+=s.getLength();
} else {
if (writeAndReportChange(f,s,size)) change=true;
size=0;
}
lState.len=l;
while (size>0) {
if (!change && lState.chararray[f]!=' ') {
change=true;
}
lState.chararray[f]=' ';
f++;
size--;
}
lState.string=null;
if (change) {
notifyChange();
}
}
public void setStringAt(Object from,Object to,Object string) {
setStringAt(
Clarion.getClarionObject(from),
Clarion.getClarionObject(to),
Clarion.getClarionObject(string));
}
/**
* Return source string repeated upto 255 characters
*
* @param source
* @return
*/
public ClarionString all()
{
return all(255);
}
/**
* Return this string repeated upto src characters long
*
* @param source
* @return
*/
public ClarionString all(int src)
{
if (getLength()==0) throw new IllegalStateException("Cannot report empty string");
char result[]=new char[src];
int scan=0;
while (scanresult.length) scanTo=result.length;
getChars(0,scanTo-scan,result,scan);
scan=scanTo;
}
return new ClarionString(result);
}
@Override
public ClarionObject negate() {
return toNumber().negate();
}
/**
* Return ascii character value of string (assume 1 char string)
* @return
*/
public int val()
{
if (getLength()>0) return charAt(0);
return 32;
}
/**
* Return true is string encodes a legitimate numeric
* @return
*/
public boolean isNumeric()
{
return isNumeric(true);
}
/**
* return true if string is upper case. If string length>1 then
* only test first char
*
* @return
*/
public boolean isUpper()
{
if (getLength()==0) return false;
char c = charAt(0);
return (c>='A' && c<='Z');
}
/**
* return true if string is only alpha characters.If string length>1 then
* only test first char
* @return
*/
public boolean isAlpha()
{
if (getLength()==0) return false;
char c = charAt(0);
return ( (c>='A' && c<='Z') || (c>='a' && c<='z') );
}
@Override
public ClarionDecimal getDecimal() {
return new ClarionDecimal(toString().trim());
}
/**
* Format string as per specified picture formatter
*
* @param aFormat
* @return
*/
public ClarionString format(String aFormat)
{
Formatter f = Formatter.construct(aFormat);
return new ClarionString(f.format(toString()));
}
/**
* Remove formatting characters from format
*
* For numeric - remove everything but numeric formatters
*
* For date, convert back to date format
*
* For time, convert back to time format
*
* For picture/key tokens return placeholder tokens
*
* @param aFormat
* @return
*/
public ClarionString deformat(String aFormat)
{
Formatter f = Formatter.construct(aFormat);
return new ClarionString(f.deformat(toString()));
}
/**
* Get string length - result dependent upon encoding system used
*
* @return
*/
public int len() {
if (encoding==STRING && size>=0) {
return size;
}
return getLength();
}
/**
* Convert ascii code into a string
*
* @param ascii
* @return
*/
public static ClarionString chr(int ascii) {
return new ClarionString(String.valueOf((char)ascii));
}
/**
* Return string left shifted.
*
* @param offset - length of resultant string.
* @return
*/
public ClarionString left(int offset)
{
char c[] = new char[offset];
int len = getLength();
int start =0;
while (startoffset) len=offset+start;
getChars(start,len,c,0);
for (int scan=len-start;scanoffset) start=len-offset;
// RIGHT... (start=0, len=5, offset=8)
// ...RIGHT
getChars(start,len,c,offset-len+start);
for (int scan=offset-len+start-1;scan>=0;scan--) {
c[scan]=' ';
}
return new ClarionString(c);
}
/**
* return right justified string
*
*/
public ClarionString right()
{
return right(len());
}
/**
* Return center justified string
* @param offset resultant length of new string
* @return
*/
public ClarionString center(int offset)
{
char c[] = new char[offset];
int len = getLength();
int start =0;
while (startoffset) {
len=offset+start-c_offset;
}
getChars(start,len,c,c_offset);
// left pad
for (int scan=0;scansize && size>=0) t=size;
return stringAt(new ClarionNumber(f),new ClarionNumber(t));
}
/**
* Return uppercased string
* @return
*/
public ClarionString upper()
{
return new ClarionString(toString().toUpperCase());
}
/**
* Return lower cased string
* @return
*/
public ClarionString lower()
{
return new ClarionString(toString().toLowerCase());
}
/**
* return clipped string - trailing whitespace removed.
* @return
*/
public ClarionString clip()
{
int len=getLength();
if (len==0) return this;
if (charAt(len-1)!=' ') {
return this;
}
len--;
while (len>0) {
if (charAt(len-1)!=' ') break;
len--;
}
return new ClarionString(toString().substring(0,len));
}
/**
* Find position of string in another string
* @param search
* @return
*/
public int inString(String search) {
return inString(search,search.length(),1);
}
/**
* find position of a string in another string
* @param search
* @param step
* @param ofs
* @return
*/
public int inString(String search,int step,int ofs) {
int scan=ofs-1;
while (scan+search.length()<=getLength()) {
boolean match=true;
for (int ss=0;ss)o;
} else {
throw new RuntimeException("Address invalid:"+o);
}
notifyChange();
return;
}
throw new RuntimeException("Not yet implemented");
}
private void serializeString(CMem is)
{
int len = getLength();
for (int scan=0;scan0) {
is.writeChar(pad);
len--;
}
}
@Override
public void serialize(CMem is)
{
if (encoding==STRING) {
serializeString(is);
return;
}
if (encoding==PSTRING) {
is.writeByte(getLength());
serializeString(is);
serializePadding(is,' ',size-1-getLength());
return;
}
if (encoding==CSTRING) {
serializeString(is);
is.writeByte(0x00);
serializePadding(is,' ',size-1-getLength());
return;
}
if (encoding==ASTRING) {
is.writeObject(state);
return;
}
// ASTRING Encoding not yet supported
throw new RuntimeException("Not yet implemented:"+encoding);
}
public ClarionString stringAt(ClarionObject o)
{
int pos = o.intValue();
if (pos<=0) return new ClarionString(" ");
if (pos>getLength()) return new ClarionString(" ");
return new ClarionString(toString().substring(pos-1,pos));
}
public ClarionString stringAt(ClarionObject from,ClarionObject to)
{
int f = from.intValue();
int t = to.intValue();
if (f<=0) f=1;
if (tsize && size>=0) t=size;
char result[] = new char[t-f+1];
//int copy=result.length;
if (t>getLength()) {
getChars(f-1,getLength(),result,0);
for (int scan=getLength()-(f-1);scan0) {
char f = charAt(0);
if (f=='-') scan++;
}
if (getLength()==scan) return false;
char f = charAt(scan);
if (f<'0' || f>'9') {
if (f!='.' || !allowDecimal) return false;
}
int lead=0;
int trail=0;
while (scan'9') break;
lead++;
scan++;
}
if (mustDecimal && scan==getLength()) return false;
if (allowDecimal && scan'9') break;
trail++;
scan++;
}
} else {
if (mustDecimal) return false;
}
}
if (lead==0 && trail==0) return false;
while (scan0) {
char c = in.charAt(last-1);
if (c!=' ') break;
last--;
}
if (last==in.length()) return in;
return in.substring(0,last);
}
@Override
public void notifyChange() {
State lState = state.get();
if (encoding==ClarionString.CSTRING) {
for (int scan=0;scan0?pattern.charAt(scan-1):0;
char c = pattern.charAt(scan);
if (c=='{' && lastChar!='\\' && !inBlock) {
sb.append('(');
continue;
}
if (c=='}' && lastChar!='\\' && !inBlock) {
sb.append(')');
continue;
}
if (c=='[' && lastChar!='\\') {
inBlock=true;
}
if (c==']' && lastChar!='\\') {
inBlock=false;
}
if (c=='(' && !inBlock && lastChar!='\\') {
sb.append('\\');
}
if (c==')' && !inBlock && lastChar!='\\') {
sb.append('\\');
}
sb.append(c);
}
return Pattern.compile(sb.toString(),ignoreCase ? Pattern.DOTALL+Pattern.CASE_INSENSITIVE : Pattern.DOTALL);
}
public int strpos(String regex)
{
return strpos(regex,false);
}
public int strpos(String regex,boolean mode)
{
Pattern p = getClarionRegEx(regex,mode);
Matcher m = p.matcher(toString());
if (m.find()) {
return m.start()+1;
}
return 0;
}
public boolean match(String regex)
{
return match(regex,Match.WILD);
}
public boolean match(String regex,int mode)
{
boolean ignoreCase=false;
if ( (mode & Match.NOCASE)!=0 ) {
ignoreCase=true;
mode=mode^Match.NOCASE;
}
switch(mode) {
case Match.SIMPLE:
if (ignoreCase) {
return toString().equalsIgnoreCase(regex);
} else {
return toString().equals(regex);
}
case Match.WILD:
return getClarionWildCardEx(regex,ignoreCase).matcher(toString()).find();
case Match.REGULAR:
return getClarionRegEx(regex,ignoreCase).matcher(toString()).find();
default:
throw new RuntimeException("Not supported");
}
}
public String quote()
{
return quote(false);
}
/**
* Quote. See clarion 6 doco. What exactly this does will remain a mystery
*
* when mode is false - generate clarion encoded string. Clarion 6 is actually buggy,
* this implementation mirrors that bugginess perfectly
*
* When mode is true - do something strange and unexplicable. See clarion 6 doco
*
* @param mode
* @return
*/
public String quote(boolean mode)
{
StringEncoding enc = new StringEncoding(toString());
while (enc.remaining()>0) {
char c = enc.read();
if (c=='<') {
if (!enc.matches(Constant.binaryPattern,-1)) {
if (!mode) enc.append(c);
} else {
if (mode) {
// do something weird - perform clarion escape
Constant.encodeBinary(enc,false);
continue;
}
}
}
if (c=='{') {
if (!enc.matches(Constant.repeatPattern,-1)) {
if (!mode) enc.append(c);
} else {
if (mode && enc.pos()>1) {
// do something weird - perform clarion escape
int repeat = Constant.getRepeat(enc);
char last = enc.peek(-2);
while (repeat>1) {
enc.append(last);
repeat--;
}
continue;
}
}
}
if (c=='\'') {
enc.append(c);
}
enc.append(c);
}
return enc.getBuilder().toString();
}
public String unquote()
{
StringEncoding enc = new StringEncoding(toString());
while (enc.remaining()>0) {
char c = enc.read();
if (c=='\'' || c=='{' || c=='<') {
if (enc.remaining()>0 && enc.peek(0)==c) {
enc.read(); // skip
}
}
enc.append(c);
}
return enc.getBuilder().toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy