/**
* $Id$
* $Date$
*
*/
package org.xmlsh.core;
import static org.xmlsh.core.XVariable.XVarFlag.EXPORT;
import static org.xmlsh.core.XVariable.XVarFlag.LOCAL;
import static org.xmlsh.core.XVariable.XVarFlag.READONLY;
import static org.xmlsh.core.XVariable.XVarFlag.UNSET;
import java.util.EnumSet;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.xml.sax.SAXException;
import org.xmlsh.sh.core.EvalUtils;
import org.xmlsh.sh.shell.Shell;
import org.xmlsh.types.IMethods;
import org.xmlsh.types.ITypeFamily;
import org.xmlsh.types.TypeFamily;
import org.xmlsh.util.Util;
public abstract class XVariable {
private static final String sName = "name";
private static final String sVariable = "variable";
private static final String sType = "type";
private static final String sSimpleType = "simple-type";
private static final String sFlags = "flags";
private static final String sTypeFamily = "type-family";
protected static Logger mLogger = LogManager.getLogger();
public static enum XVarFlag {
EXPORT, // to be exported to child shells
READONLY, LOCAL, UNSET;
};
/*
* Variable Expansion parsed to components
* ${ [#|!] varname [ '[' index ']' ] [ ':' expr } =>
* [ prefix , name , index , field ]
*
* $x => ( null , x , null , null )
* ${!x} => ( ! , x , null , null )
* ${x[foo] => ( null , x , foo , null )
* ${x:bar} => (null , x , null , bar )
* ${!x[foo]:bar} ( ! , x , foo , bar )
*/
private String mName;
private EnumSet<XVarFlag> mFlags;
private final static EnumSet<XVarFlag> XVAR_STANDARD = EnumSet
.noneOf(XVarFlag.class);
private final static EnumSet<XVarFlag> XVAR_LOCAL = EnumSet.of(LOCAL);
private final static EnumSet<XVarFlag> XVAR_INIT = EnumSet.of(UNSET);
private final static EnumSet<XVarFlag> XVAR_SYSTEM = EnumSet.of(EXPORT);
protected XVariable(String name, EnumSet<XVarFlag> flags) {
mName = name;
mFlags = flags;
}
// helper for flag tests that requre UNSET to be OFF
private boolean hasFlags(XVarFlag... flags) {
if(isUnset())
return false;
return Util.setContainsAll(mFlags, flags);
}
private boolean hasAnyFlags(XVarFlag... flags) {
if(isUnset())
return false;
return Util.setContainsAny(mFlags, flags);
}
// helper for flag tests that requre UNSET to be OFF
private boolean hasFlag(XVarFlag flag) {
if(isUnset())
return false;
return mFlags.contains(flag);
}
public static EnumSet<XVarFlag> addFlag(EnumSet<XVarFlag> flags,
XVarFlag flag) {
assert (flag != UNSET);
return Util.withEnumAdded(flags, flag);
}
public static EnumSet<XVarFlag> standardFlags() {
return XVAR_STANDARD;
}
public static EnumSet<XVarFlag> localFlags() {
return XVAR_LOCAL;
}
/*
* Flag accessors
*/
private boolean isUnset() {
return mFlags.contains(UNSET);
}
public boolean isExport() {
return hasFlag(EXPORT);
}
public boolean isLocal() {
return hasFlag(LOCAL);
}
public void setLocal(boolean on) throws InvalidArgumentException {
if(on)
setFlag(LOCAL);
else
clearFlag(LOCAL);
}
private void clearFlag(XVarFlag f) throws InvalidArgumentException {
checkWrite();
mFlags = Util.withEnumRemoved(mFlags, f);
}
/**
* @return the name
*/
public String getName() {
return mName;
}
/**
* @param name
* the name to set
*/
public void setName(String name) {
mName = name;
}
public abstract XValue getValue();
// Set an indexed value
// a[ind] = b
public void setIndexedValue(XValue value, String ind) throws CoreException {
checkWrite();
assert (!Util.isBlank(ind));
getValueMethods().setXValue(getValue(), ind, value);
}
public abstract void setValue(XValue setXValue)
throws InvalidArgumentException;
public IMethods getValueMethods() {
return getValue().typeFamilyInstance();
}
/**
* @return the flags
*/
public EnumSet<XVarFlag> getFlags() {
return mFlags;
}
public void setFlag(XVarFlag flag) throws InvalidArgumentException {
checkWrite();
mFlags = Util.withEnumAdded(mFlags, flag);
}
public void serialize(XMLStreamWriter writer)
throws SAXException, XMLStreamException {
XValue value = this.getValue();
String flagStr = mFlags.toString();
writer.writeStartElement(sVariable);
writer.writeAttribute(sName, getName());
writer.writeAttribute(sTypeFamily,
value == null ? "null" : value.typeFamily().name());
String type = "null";
String simpleType = type;
/*
* if( bSimple || ! arg.isXdmItem() ){
*
* String type =
* bSimple ?
* arg.typeFamilyInstance().simpleTypeName( arg.asObject() ) :
* arg.typeFamilyInstance().typeName( arg.asObject() );
*
* w.println( type );
*
* }
*/
if(value != null && !value.isNull()) {
Object obj = value.asObject();
ITypeFamily it = value.typeFamilyInstance();
type = it.typeName(obj);
assert (type != null);
simpleType = it.simpleTypeName(obj);
}
writer.writeAttribute(sType, type);
writer.writeAttribute(sSimpleType, simpleType);
writer.writeAttribute(sFlags, flagStr);
writer.writeEndElement();
}
public void clear() throws InvalidArgumentException {
checkWrite();
setValue(null);
}
public boolean isNull() {
return getValue() == null;
}
public boolean shift(int n) throws InvalidArgumentException {
if(n <= 0 || getValue() == null)
return false;
setValue(getValue().shift(n));
return true;
}
/*
* Get a variable value with an optional index and tie expression
*/
public XValue getValue(Shell shell, EvalEnv env, String ind, String tie)
throws CoreException {
XValue xv = getValue();
if(!Util.isBlank(ind)) {
// Special case * @
if(Util.isOneOf(ind, "*", "@"))
return EvalUtils.getValues(env, xv);
else
return XVariable.getIndexedValue(env, xv, ind);
}
assert (Util.isEmpty(tie));
return xv;
}
public int getSize() throws InvalidArgumentException {
return EvalUtils.getSize(getValue());
}
public void unset() throws InvalidArgumentException {
clear();
setFlag(UNSET);
}
public void export() throws InvalidArgumentException {
setFlag(EXPORT);
}
static class XValueVariable extends XVariable implements Cloneable {
private XValue mValue;
private XValueVariable(String name, XValue value, EnumSet<XVarFlag> flags) {
super(name, flags);
mValue = value;
}
@Override
public XVariable clone() {
XVariable that = new XValueVariable(getName(), getValue(), getFlags());
return that;
}
@Override
public XVariable clone(EnumSet<XVarFlag> flags) {
XVariable that = new XValueVariable(getName(), getValue(),
getFlags(flags));
return that;
}
/**
* @return the raw value
*/
public XValue getValue() {
return mValue;
}
/**
* @param value
* the value to set
* @return
* @throws InvalidArgumentException
* Flags are presumed to be already set
*/
@Override
public void setValue(XValue value) throws InvalidArgumentException {
checkWrite();
mValue = value;
}
}
public XVariable newValue(XValue value, EnumSet<XVarFlag> flags) {
XVariable that = new XValueVariable(mName, value, getFlags(flags));
return that;
}
protected EnumSet<XVarFlag> getFlags(EnumSet<XVarFlag> flags) {
return Util.withEnumsAdded(mFlags, flags);
}
public static XVariable newInstance(String name) {
return new XValueVariable(name, null, XVAR_STANDARD);
}
public static XVariable newInstance(String name, XValue value) {
return new XValueVariable(name, value, XVAR_STANDARD);
}
public static XVariable newInstance(String name, XValue value,
EnumSet<XVarFlag> flags) {
return new XValueVariable(name, value, flags);
}
public static XVariable newInstance(String name, String value)
throws InvalidArgumentException {
return new XValueVariable(name, XValue.newXValue(value), XVAR_STANDARD);
}
// Catch bad calls !
public static XVariable newInstance(String name, EnumSet<XVarFlag> flags) {
mLogger.entry(name, flags);
mLogger.warn("overloading newInstance(String,T) - fix caller");
return newInstance(name, null, flags);
}
public static <T extends Object> XVariable newInstance(String name, T value)
throws InvalidArgumentException {
mLogger.entry(name, value);
return new XValueVariable(name, XValue.newXValue(value), XVAR_STANDARD);
}
public static XVariable anonymousInstance() {
return new XValueVariable(null, null, XVAR_STANDARD);
}
public static XVariable anonymousInstance(TypeFamily type) {
return new XValueVariable(null, XValue.nullValue(type), XVAR_STANDARD);
}
public static <T extends XValue> XVariable anonymousInstance(T value) {
return new XValueVariable(null, value, XVAR_STANDARD);
}
public static <T extends Object> XVariable anonymousInstance(T value)
throws InvalidArgumentException {
return new XValueVariable(null, XValue.newXValue(value), XVAR_STANDARD);
}
public static EnumSet<XVarFlag> systemFlags() {
return XVAR_SYSTEM;
}
abstract public XVariable clone(EnumSet<XVarFlag> flags)
throws InvalidArgumentException;
public static XVariable newLocalInstance(String name, XValue value) {
return newInstance(name, value, localFlags());
}
public static EnumSet<XVarFlag> unsetFlags() {
return XVAR_INIT;
}
public void setFlags(EnumSet<XVarFlag> flags)
throws InvalidArgumentException {
checkWrite();
mFlags = getFlags(flags);
}
@Override
public XVariable clone() {
try {
return clone(getFlags());
} catch (InvalidArgumentException e) {
mLogger.warn(e);
}
return null;
}
protected void checkWrite() throws InvalidArgumentException {
if(mFlags.contains(READONLY))
throw new InvalidArgumentException(
"Cannot modify readonly variable: " + getName());
}
// Set value and flags
public void setValue(XValue value, EnumSet<XVarFlag> flags)
throws InvalidArgumentException {
checkWrite();
setValue(value);
mFlags = getFlags(flags);
}
public static XValue getIndexedValue(EvalEnv env, XValue xvalue, String ind)
throws CoreException {
assert (xvalue != null);
assert (!Util.isBlank(ind));
if(xvalue == null)
return XValue.nullValue();
if(Util.isBlank(ind))
return xvalue;
if(xvalue.isAtomic()) {
if(ind.equals("1"))
return xvalue.newInstance();
return XValue.nullValue(xvalue.typeFamily());
}
return xvalue.getTypeMethods().getXValue(xvalue.asObject(), ind);
}
public static XValue getIndexedValue(EvalEnv env, XValue xvalue, int index)
throws CoreException {
assert (xvalue != null);
// assert (index >= 0);
if(xvalue == null)
return XValue.nullValue();
if(index < 0)
return XValue.nullValue(xvalue.typeFamily());
// throw new InvalidArgumentException("Invalid index for indexed expression:
// " + index);
/*
* if( xvalue.isAtomic() ) {
* if( index == 0 )
* return xvalue.newInstance();
* return XValue.nullValue(xvalue.typeFamily()) ;
* }
*/
return xvalue.getTypeMethods().getXValue(xvalue.asObject(), index);
}
}
//
//
// Copyright (C) 2008-2014 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): none.
//