/**
* $Id: $
* $Date: $
*
*/
package org.xmlsh.sh.shell;
import static org.xmlsh.util.CharAttr.ATTR_PRESERVE;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.xmlsh.core.EvalEnv;
import org.xmlsh.core.EvalFlag;
import org.xmlsh.core.XValue;
import org.xmlsh.sh.core.CharAttributeBuffer;
import org.xmlsh.sh.core.CharAttrs;
import org.xmlsh.sh.core.EvalUtils;
import org.xmlsh.util.Util;
public class ParseResult {
// Attribute'd char buffer
private CharAttributeBuffer achars = null;
static Logger mLogger = LogManager.getLogger();
public static class RXValue {
public XValue xvalue;
private CharAttributeBuffer avalue;
public CharAttrs xvalAttrs;
RXValue(XValue v, boolean r) {
xvalue = v;
xvalAttrs = r ? CharAttrs.constInstance(ATTR_PRESERVE)
: CharAttrs.constInstance();
}
RXValue(CharAttributeBuffer av) {
avalue = av;
xvalAttrs = CharAttrs.constInstance();
}
RXValue(String s, CharAttrs attr) {
avalue = new CharAttributeBuffer(s, attr);
}
@Override
public String toString() {
if(xvalue != null)
return xvalue.toString();
else if(avalue != null)
return avalue.toString();
return null;
}
public XValue toXValue() {
return xvalue != null ? xvalue
: XValue.newXValue(avalue == null ? null : avalue.toString());
}
public CharAttributeBuffer toAValue() {
return avalue != null ? avalue.clone()
: new CharAttributeBuffer(xvalue.toString(), xvalAttrs);
}
public boolean isRaw() {
return xvalAttrs.isPreserve();
}
public boolean isXValue() {
return xvalue != null;
}
public boolean isEmpty() {
return xvalue == null && avalue == null;
}
}
ParseResult.RXValue cur = null; // Current XValue if its unknown
// to convert to string, only
// atomic values
List<ParseResult.RXValue> result = null;
public void delim() {
flush(); // TODO insert a null token?
}
private List<ParseResult.RXValue> getResult() {
if(result == null)
result = new ArrayList<ParseResult.RXValue>(1);
return result;
}
public void flush() {
if(cur != null) {
getResult().add(cur); // dont call ajoin adds twice
cur = null;
}
// resetIfEmpty();
if(achars != null) {
getResult().add(new RXValue(achars));
achars = null;
}
}
public void add(CharAttributeBuffer cb) {
flush();
getResult().add(new RXValue(cb));
}
public void add(String s, CharAttrs attr) {
flush();
getResult().add(new RXValue(s, attr));
}
public void add(XValue v) {
flush();
getResult().add(new RXValue(v, false));
}
public void add(XValue v, boolean r) {
flush();
getResult().add(new RXValue(v, r));
}
public void append(String s, CharAttrs attr) {
ajoin();
if(achars == null)
achars = new CharAttributeBuffer();
if(s != null)
achars.append(s, attr);
}
public void append(CharAttributeBuffer cb) {
ajoin();
if(achars == null)
achars = new CharAttributeBuffer(cb);
else
achars.append(cb);
}
public void append(char c, CharAttrs attr) {
ajoin();
if(achars == null)
achars = new CharAttributeBuffer();
achars.append(c, attr);
}
public List<ParseResult.RXValue> resolveToRXValues() {
flush();
return result;
}
/*
* Append a value to the result buffer
* If currently in-quotes then convert the args to strings and seperated by
* the first char in IFS.
*/
public void append(XValue value, EvalEnv env, CharAttrs attr) {
if((value == null || value.isNull()) && env.omitNulls())
return;
if(attr.isPreserve()) {
flush();
add(value, attr.isPreserve());
}
else if(value.isAtomic()) {
// If in quotes or this is an ajoining value then concatenate
if(attr.isQuote() || cur != null || achars != null) {
// Unquoted empty atomic values are ignored
String str = value.toString();
if(attr.isQuote() && Util.isEmpty(str))
return;
append(str, attr);
}
else
cur = new RXValue(value, false);
}
else {
// Inquotes - expand sequences via spearator or not
if(attr.isQuote()) {
if(!env.expandSequences() || !value.isSequence())
append(value.toString(), attr);
else {
// Flatten sequences
boolean bFirst = true;
for(XValue v : value) {
if(!bFirst)
append(ShellConstants.ARG_SEPARATOR, attr);
append(v.toString(), attr);
bFirst = false;
}
}
}
// Not in quotes but joining values // expand sequences ?
else if(env.joinValues() && env.expandSequences()) {
// Only join first value
boolean bFirst = true;
for(XValue v : value) {
if(!bFirst)
flush();
append(v, env.withFlagOff(EvalFlag.EXPAND_SEQUENCES), attr);
bFirst = false;
}
}
else {
flush();
add(value, attr.isPreserve());
}
}
}
public void append(ParseResult r) {
if(r == null)
return;
ajoin();
r.ajoin(); // ??
if(r.achars != null)
append(r.achars);
if(!r.result.isEmpty()) {
flush();
result.addAll(r.result);
}
}
private void ajoin() {
if(cur != null) {
if(achars == null)
achars = new CharAttributeBuffer(cur.toAValue());
else
achars.append(cur.toAValue());
cur = null;
}
}
public void resetIfEmpty() {
if(achars != null && achars.isEmpty())
achars = null;
}
public static List<XValue> expandWild(RXValue rv, File curdir) {
assert (rv != null);
assert (!rv.isEmpty());
assert (!rv.isRaw());
CharAttributeBuffer av = rv.toAValue();
if(!Util.containsWild(av))
return Collections.singletonList(rv.toXValue());
ArrayList<XValue> r = new ArrayList<XValue>();
/*
* If vs starts with / (or on dos x:) then use that directory as the root
* instead of the current directory
*/
String root = null;
String parent = null;
if(av.charAt(0) == '/') {
root = "/";
parent = "";
av.delete(0, 1);
}
/* Moove to FilesSystem.getRoot */
if(Util.isWindows() && av.size() >= 2) {
char drive = av.charAt(0);
// Character.isAlphabetic() is V7 only
if(Character.isLetter(drive) && av.charAt(1) == ':') {
// If windows and matches <dir>:blah blah
// make the root <dir>:/
// If no "/" is used then the current directory of that dir is used
// which is not shell semantics
root = "" + drive + ":/";
if(av.size() > 2 && av.charAt(2) == '/')
av.delete(0, 3);
else
av.delete(0, 2);
parent = root;
}
}
/*******/
if(!av.isEmpty()) {
CharAttributeBuffer wilds[] = av.split('/',true);
if(wilds.length > 0) {
List<String> rs = new ArrayList<String>();
try {
EvalUtils.expandDir(root == null ? curdir : new File(root),
parent,
wilds,
rs);
for(String f : rs) {
r.add(XValue.newXValue(f));
}
} catch (IOException e) {
mLogger.catching(e);
}
}
}
// If no matches then use arg explicitly
if(r.size() == 0)
r.add(rv.toXValue());
return r;
}
/*
* Expand a single level wildcard rooted at a directory
* Return list of all matches or null if no matches
*/
public List<XValue> expandWild(EvalEnv env, File curdir) {
List<RXValue> resolved = resolveToRXValues();
if(resolved == null)
return null;
ArrayList<XValue> result2 = new ArrayList<XValue>();
/*
* Globbing
*/
for(RXValue rv : resolved) {
if(!env.expandWild() || rv.isRaw())
result2.add(rv.toXValue());
else {
if(rv.isXValue() && !rv.toXValue().isAtomic())
result2.add(rv.toXValue());
else {
List<XValue> r = ParseResult.expandWild(rv, curdir);
if(r != null)
result2.addAll(r);
}
}
}
return result2;
}
// For logging
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ParseResult");
if(cur != null)
sb.append("cur=[" + cur.toString() + "]");
if(achars != null) {
sb.append("achars=[");
achars.logString(sb);
sb.append("]");
}
if(result != null) {
sb.append("result=[");
Util.join(sb, result, ",").append("]");
}
return sb.toString();
}
}
/*
* 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
*
*/