2005/5/9

     
 

Props.cpp

artefaktur
// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*-
//
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
// 
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
//                         ACDK
//
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
//
#include <acdk.h>
#include <acdk/lang/System.h>
#include <acdk/util/StringTokenizer.h>
#include <acdk/util/TreeSet.h>
#include <acdk/io/File.h>

#include "Script.h"
#include "ScriptException.h"
#include "ShellExecutor.h"

namespace acdk {
namespace cfgscript {

USING_CLASS(::acdk::util::, Iterator);
USING_CLASS(::acdk::util::, StringTokenizer);

Props::Props(IN(acdk::lang::dmi::RDmiNamedArgArray) namedArgs, short flags)
: _defaultFlags(flags)
, _castFlags(acdk::lang::dmi::SVCastStdFlags)
, _parents(new PropsArray(0))
, _curHeap(new HashMap())
, _name("")
, _singleThreaded(true)
{
  if (isStackRef() == true)
    ACDK_NLOG("acdk.cfgscript.Props", Note, "Props is allocated on stack, which may crash in connection with Scripts");

  ACDK_SAFE_CONSTRUCTOR();
  for (int i = 0; i < namedArgs->length(); ++i)
    set(namedArgs[i]->name, namedArgs[i]->value);
}

Props::Props(IN(RString) name, short flags, IN(RProps) parent, bool private_props)
: _defaultFlags(flags)
, _castFlags(acdk::lang::dmi::SVCastStdFlags)
, _parents(new PropsArray(0))
, _curHeap(new HashMap())
, _name(name)
, _singleThreaded(true)
{
  if (isStackRef() == true)
    ACDK_NLOG("acdk.cfgscript.Props", Note, "Props is allocated on stack, which may crash in connection with Scripts");

  if (parent != Nil)
  {
    _singleThreaded = parent->getSingleThreaded();
    _parents->append(parent);
    _castFlags = parent->getCastFlags();
  }
  if (_parents->length() == 0 && private_props == false)
    _init();
}

Props::Props(short flags, IN(RProps) parent, bool private_props)
: _defaultFlags(flags)
, _castFlags(acdk::lang::dmi::SVCastStdFlags)
, _parents(new PropsArray(0))
, _curHeap(new HashMap())
, _name("")
, _singleThreaded(true)
{
  if (isStackRef() == true)
    ACDK_NLOG("acdk.cfgscript.Props", Note, "Props is allocated on stack, which may crash in connection with Scripts");

  if (parent != Nil)
  {
    _parents->append(parent);
    _singleThreaded = parent->getSingleThreaded();
    _castFlags = parent->getCastFlags();
  }
  if (_parents->length() == 0 && private_props == false)
    _init();
}


bool 
Props::hasParentProps(IN(RProps) nparent)
{
  if (&nparent == this)
    return true;
  if (_parents == Nil)
    return false;
  for (int i = 0; i < _parents->length(); ++i)
    if (_parents[i]->hasParentProps(nparent) == true)
      return true;
  return false;
}

RString
Props::toString()
{
  StringBuffer sb;
  sb << "{ ";
  // ### @todo dump parents
  acdk::util::RMapEntry node;
  bool first = true;
  for (RIterator it = _curHeap->iterator();
       it->hasNext() == true;
       )
  {
    node = (acdk::util::RMapEntry)it->next();
    if (first == false)
      sb << ",";
    RDmiObject val = (RDmiObject)node->getValue();
    acdk::lang::Object recObj = val->getObjectVar();
    while (instanceof(recObj, DmiObject) == true)
      recObj = RDmiObject(recObj)->getObjectVar();
    if (recObj == this)
    {
      acdk::lang::Object recObj = val->getObjectVar();
      sb << node->getKey() << ": (<THISPROPS>)";
    }
    else
    //acdk::lang::Object o = _curHeap->get(&key);
      sb << node->getKey() << ": " << (node->getValue() == Nil ? RString("Nil") : node->getValue()->toString());
    first = false;
  }
  sb << " }";
  return sb.toString();
}

acdk::lang::Object 
Props::clone(acdk::lang::sys::Allocator* alloc)
{
  RProps cloned = new Props(_defaultFlags);
  cloned->_parents = (RPropsArray)_parents->clone(alloc);
  cloned->_curHeap = (RHashMap)_curHeap->clone(alloc);
  cloned->_name = _name;
  cloned->_singleThreaded = _singleThreaded;
  return &cloned;
}

RDmiObject
Props::get(IN(RString) name, short flags)
{
  SYNCTHIS();
  return _get(name, flags);
}

RDmiObject
Props::_get(IN(RString) name, short flags)
{
  RDmiObject ret = (RDmiObject)_curHeap->get(&name);
  if (ret == Nil && _readParent(flags))
  {
    for (int i = 0; i < _parents->length(); ++i)
    {
      ret = _parents[i]->get(name, flags | PropsNoWarnRead);
      if (ret != Nil)
        return ret;
    }
  }
  if (ret == Nil && _warnRead(flags))
    ACDK_NLOG("acdk.cfgscript", Warn, getName() + ": Props::get(): Key [" + name + "] does not exists");
  return ret;
}


RHashMap
Props::findOwnerHeap(IN(RString) key)
{
  if (_curHeap->get(&key) != Nil)
    return _curHeap;
  if (_parents != Nil)
  {
    for (int i = 0; i < _parents->length(); ++i)
    {
      RHashMap ret = _parents[i]->findOwnerHeap(key);
      if (ret != Nil)
        return ret;
    }
  }
  return Nil;
}

void
Props::set(IN(RString) name, IN(RDmiObject) value, short flags)
{
  SYNCTHIS();
  _set(name, value, flags);
}

void 
Props::_notifyListener(PropChangeEvents event, IN(RString) key)
{
  if (_listeners == Nil)
    return;
  for (int i = 0; i < _listeners->length(); ++i)
    _listeners[i]->afterChange(event, this, key);
}

void
Props::_set(IN(RString) name, IN(RDmiObject) value, short flags, bool notify)
{
  ACDK_NLOG("acdk.cfgscript", Debug, getName() + ": set: [" + name + "]=[" + (value == Nil ? RString("Nil") : value->toString()) + "]");
  //dbg if (name->equals("AMAKE_TARGET_TAGS") == true)
  // System::out->println("AMAKE_TARGET_TAGS =" + value->toCode());
  RHashMap h = _curHeap;

  if (_writeParent(flags) == true)
    h = findOwnerHeap(name);
  if (h == Nil)
    h = _curHeap;
  // ### eval _warnWrite
  h->put(&name, &value);
  if (notify == true)
    _notifyListener(PropEventValueWrite, name);
  
}

void
Props::unset(IN(RString) name, short flags)
{
  SYNCTHIS();
  if (_writeParent(flags) == false)
    _curHeap->remove(&name);
  else
  {
    RHashMap h = findOwnerHeap(name);
    if (h == Nil)
      return;
    h->remove(&name);
  }
  _notifyListener(PropEventRemoveKey, name);
}

void
Props::create(IN(RString) name, IN(RDmiObject) val)
{
  SYNCTHIS();
  if (hasValue(name, PropsNoParentRead) == true)
    THROW1(ScriptException, "Variable " + name + " is already defined in this property scope");
  _set(name, val, PropsNoParentWrite, false);
  _notifyListener(PropEventCreate, name);
}

void
Props::assign(IN(RString) name, IN(RDmiObject) val, short flags)
{
  SYNCTHIS();
  if (hasValue(name) == false)
    THROW1(ScriptException, "Variable " + name + " is not defined in this property scope");
  RDmiObject var = _get(name, flags);
  var->assign(val, _castFlags);
  _notifyListener(PropEventAssign, name);
}

int
Props::size(short flags)
{
  SYNCTHIS();
  if (_readParent(flags) == true)
  {
    int size = _curHeap->size();
    for (int i = 0; i < _parents->length(); ++i)
      size += _parents[i]->size(flags);
    return size;
  }
  else
    return _curHeap->size();
}

void
Props::_getKeys(IN(RStringArray) sa)
{
  SYNCTHIS();
  RIterator it = _curHeap->keySet()->iterator();
  for (int i = 0; it->hasNext() == true; ++i)
  {
    RString s = (RString)it->next();
    if (sa->find(s) == -1)
      sa->append(s);
  }
}

RStringArray
Props::getKeys(short flags)
{
  SYNCTHIS();
  RStringArray erg = new StringArray(0);
  if (_readParent(flags) && _parents != Nil)
  {
    for (int i = 0; i < _parents->length(); ++i)
    {
      _parents[i]->_getKeys(erg);
    }
  }
  _getKeys(erg);
  return erg;
}

void
Props::appendObjectList(IN(RString) key, IN(acdk::lang::Object) val, short flags)
{
  SYNCTHIS();
  RDmiObject obj = get(key, flags & PropsWarnRead ? flags : flags | PropsNoWarnRead);
  RObjectArray a;
  if (obj == Nil || obj->getObjectVar() == Nil)
    a = new ObjectArray(0);
  else
    a = (RObjectArray)obj->getObjectVar();
  a->append(val);
  if (obj == Nil)
    setObjectVal(key, &a, flags);
}

RString
Props::getStringVal(IN(RString) name, short flags)
{
  RDmiObject ret = get(name, flags);
  if (ret != Nil && ret->getObjectVar() != Nil)
  {
    return ret->getObjectVar()->toString();
  }
  return "";
}

void
Props::setStringVal(IN(RString) key, IN(RString) value, short flags)
{
  ACDK_NLOG("acdk.cfgscript", Debug, getName() + ": setStringVal: [" + key + "]=[" + value + "]");
  set(&key, new DmiObject(inOf(value)), flags);
}

void
Props::appendStringVal(IN(RString) key, IN(RString) value, IN(RString) joiner, short flags)
{

  RString str = (RString)getStringVal(key, flags & PropsWarnRead ? flags : flags | PropsNoWarnRead);
  if (str == Nil)
  {
    setStringVal(key, value, flags);
  }
  else
  {
    if (flags & PropsAppendPushFront)
    {
        if (joiner != Nil)
        str = value + joiner + str;
      else
        str = value + str;
    }
    else
    {
      if (joiner != Nil)
        str = str + joiner + value;
      else
        str = str + value;
    }
    setStringVal(key, str, flags);
  }
}

bool containsString(IN(RStringArray) sa, IN(RString) v)
{
  for (int i = 0; i < sa->length(); ++i)
    if (sa[i]->equals(v) == true)
      return true;
  return false;
}

void
Props::appendStringArrayVal(IN(RString) key, IN(RString) value, short flags)
{
  SYNCTHIS();
  RDmiObject arr = _get(key, flags & PropsWarnRead ? flags : flags | PropsNoWarnRead);
  if (arr == Nil)
  {
    RStringArray narr = new StringArray(1);
    narr[0] = value;
    setStringArrayVal(key, narr, flags);
  } else {

    RStringArray sa = RStringArray(arr->getObjectVar());
    if (flags & PropsNoStringDups)
    {
      if (containsString(sa, value) == true)
        return;
    }
    ACDK_NLOG("acdk.cfgscript", Debug, getName() + ": appendStringArray: [" + key + "] + [" + value + "]");
    if (_writeParent(flags) == false && _curHeap->get(&key) == Nil)
    {
      sa = (RStringArray)sa->clone();
      _set(key, new DmiObject(inOf(sa)), flags);
    }
    sa->append(value);
  }
}

void
Props::setStringArrayVal(IN(RString) key, IN(RStringArray) value, short flags)
{
  ACDK_NLOG("acdk.cfgscript", Debug, getName() + ": setStringArrayVal: [" + key + "]=[" + value->toString() + "]");
  set(&key, new DmiObject(inOf(value)), flags);
}

RStringArray
Props::getStringArrayVal(IN(RString) key, short flags)
{

  RDmiObject ret = get(key, flags);

  if (ret == Nil)
  {
    SYNCTHIS();

    RStringArray sa = new StringArray(0);
    set(&key, new DmiObject(inOf(sa)), flags);
    return sa;
  }
  return RStringArray(ret->getObjectVar());
}

RStringArray
Props::getAllStringArrayVal(IN(RString) key, short flags)
{
  RDmiObject ret = (RDmiObject)_curHeap->get(&key);
  RStringArray colret;
  if (ret != Nil)
    colret = (RStringArray)ret->getObjectVar();
  else
    colret = new StringArray(0);
  if (_readParent(flags) == false)
    return colret;
  for (int i = 0; i < _parents->length(); ++i)
  {
    RStringArray sub = _parents[i]->getAllStringArrayVal(key, flags | PropsNoWarnRead);
    if (sub != Nil && sub->length() > 0)
    {
      for (int i = 0; i < sub->length(); ++i)
      {
        if (containsString(colret, sub[i]) == false)
          colret->append(sub[i]);
      }
    }
  }
  return colret;
}

bool
Props::containsInStringArrayVal(IN(RString) key, IN(RString) value, short flags)
{
  SYNCTHIS();
  RStringArray erg = getStringArrayVal(key, flags);
  if (erg == Nil)
    return false;
  for (int i = 0; i < erg->length(); ++i)
  {
    if (erg[i]->equals(value) == true)
      return true;
  }
  return false;
}

void
Props::setQuotedStringVal(IN(RString) key, IN(RString) value, short flags)
{
  RString val = value;
  if (val->indexOf(' ') != -1)
  {
    RString qc = getStringVal("CMDLINE_QUOTE_CHAR", flags);
    if (val->indexOf(qc) == -1)
    {
      val = qc + val + qc;
    }
  }
  setStringVal(key, val, flags);
}

RString
Props::getQuotedStringVal(IN(RString) key, short flags)
{
  RString val = getStringVal(key, flags);
  if (val->indexOf(' ') == -1)
    return val;
  RString qc = getStringVal("CMDLINE_QUOTE_CHAR", flags);
  if (val->indexOf(qc) == 0)
    return val;
  return qc + val + qc;
}

RString
Props::getUnquotedStringVal(IN(RString) key, short flags)
{
  RString val = getStringVal(key, flags);

  RString qc = getStringVal("CMDLINE_QUOTE_CHAR", flags);
  if (val->startsWith(qc) && val->endsWith(qc) == true)
    return val->substr(1, val->length() - 2);
  return val;
}

RProps
Props::getProps(IN(RString) name, short flags)
{
  RDmiObject obj = get(name, flags);
  if (obj != Nil)
    return RProps(obj->getObjectVar());

  RProps np = new Props(PropsParentRead | PropsWarnRead);
  set(&name, new DmiObject(inOf(np)), flags);
  return np;
}

//static
RStringArray
Props::makeStringArray(IN(RString) s1, IN(RString) s2, IN(RString) s3, IN(RString) s4,
                                      IN(RString) s5, IN(RString) s6, IN(RString) s7, IN(RString) s8)
{
  RStringArray erg;
  if (s8 != Nil)
  {
    if (erg == Nil)
      erg = new StringArray(8);
    erg[7] = s8;
  }
  if (s7 != Nil)
  {
    if (erg == Nil)
      erg = new StringArray(7);
    erg[6] = s7;
  }
  if (s6 != Nil)
  {
    if (erg == Nil)
      erg = new StringArray(6);
    erg[5] = s6;
  }
  if (s5 != Nil)
  {
    if (erg == Nil)
      erg = new StringArray(5);
    erg[4] = s5;
  }
  if (s4 != Nil)
  {
    if (erg == Nil)
      erg = new StringArray(4);
    erg[3] = s4;
  }
  if (s3 != Nil)
  {
    if (erg == Nil)
      erg = new StringArray(3);
    erg[2] = s3;
  }
  if (s2 != Nil)
  {
    if (erg == Nil)
      erg = new StringArray(2);
    erg[1] = s2;
  }
  if (s1 != Nil)
  {
    if (erg == Nil)
      erg = new StringArray(1);
    erg[0] = s1;
  }
  if (erg == Nil)
    erg = new StringArray(0);
  return erg;
}


RStringArray
splitPathList(IN(RString) str)
{
  StringTokenizer st(str, ::acdk::io::File::pathSeparator());
  RStringArray  ret = new StringArray(0);
  while (st.hasNext() == true)
  {
    ret->append(st.nextToken());
  }
  return ret;
}

int 
Props::_getFlagsFromStringProps(int defaultFlags)
{
  // ### TODO implement me
  return defaultFlags;
}

void
Props::_init()
{
  ACDK_NLOG("acdk.cfgscript", Info, getName() + ": Initialize Env Props");
  RProperties props = System::getProperties();
  RIterator it = props->propertyNames();
  int stdflags = 0;
  while (it->hasNext() == true)
  {
    RString k = (RString)it->next();
    setStringVal(k, props->getProperty(k), stdflags);
  }
#if defined(ACDK_OS_WIN32)
  if (getStringVal("Path", PropsNoWarnRead) != Nil && getStringVal("Path", PropsNoWarnRead)->length() > 0) // on cygwin, this is not Path, but PATH
    setStringVal("PATH", getStringVal("Path", 0), stdflags);
#endif
/FONT>
  setStringArrayVal("ENV_PATH_LIST", splitPathList(getStringVal("PATH", stdflags)), stdflags);
  if (hasValue("INCLUDE", stdflags) == true)
    setStringArrayVal("ENV_INCLUDE_LIST", splitPathList(getStringVal("INCLUDE", stdflags)), stdflags);
  if (hasValue("LIB", stdflags) == true)
    setStringArrayVal("ENV_LIB_LIST", splitPathList(getStringVal("LIB", stdflags)), stdflags);
  if (hasValue("LD_LIBRARY_PATH", stdflags) == true)
    setStringArrayVal("ENV_LD_LIBRARY_PATH_LIST", splitPathList(getStringVal("LD_LIBRARY_PATH", stdflags)), stdflags);
  if (hasValue("CLASSPATH", stdflags) == true)
    setStringArrayVal("ENV_CLASSPATH_LIST", splitPathList(getStringVal("CLASSPATH", stdflags)), stdflags);
}


RString removeQuoted(IN(RString) str)
{
  int idx = str->indexOf("#{");
  if (idx == -1)
    return str;
  StringBuffer sb;
  String::iterator it = str->begin();
  String::iterator end = str->end();
  String::iterator lstart = it;
  enum State 
  { 
    Seek,
    Collect
  };
  State state = Seek;
  for (; it < end; ++it)
  {
    if (state == Seek)
    {
      if ((it + 1) < end && *(it + 1) == '{' &&
        (*it == '#'))
      {
        sb.append(lstart, it);
        lstart = it + 2;
        state = Collect;
      }
    }
    else
    {
      if ((it + 1) < end && *(it + 1) == '#' && (*it == '}'))
      {
        sb.append(lstart, it);
        it += 2;
        lstart = it;
        state = Seek;
      }
    }
  }
  sb.append(lstart, end);
  return sb.toString();
}

RString
Props::evalShellExecute(IN(RString) str, short flags)
{
  int idx = str->indexOf('`');
  if (idx == -1)
    return str;
  StringBuffer sb;
  String::iterator it = str->begin();
  String::iterator end = str->end();
  String::iterator lstart = it;
  enum State 
  { 
    Seek,
    Collect
  };
  State state = Seek;
  for (; it < end; ++it)
  {
    if (state == Seek)
    {
     if ((it + 1) < end && *(it + 1) == '{' &&
        (*it == '`'))
      {
        sb.append(lstart, it);
        state = Collect;
      }
    }
    else 
    {
      if ((it + 1) < end && *(it + 1) == '`' && (*it == '}'))
      {
        RString iden = str->substr(lstart - str->begin() + 2, it - str->begin());
        iden = removeQuoted(iden);
        ShellExecutor exec(iden, SExecNoStdOut | SExecNoErrOut, Nil);
        if (hasValue("err") == true)
        {
          acdk::lang::Object o = getObjectVal("err");
          if (instanceOf(o, acdk::io::CharWriter) == true)
            exec.setErrWriter((acdk::io::RCharWriter)o);
        }
        exec.setOutWriter(Nil);
        bool erg = exec.execute(this);
        RString outp = exec.getOutString();
        outp = outp->trim(TrimNewLines | TrimRight);
        sb.append(outp);
        ++it;
        lstart = it + 1;
        state = Seek;
      }
    }
  }
  sb.append(lstart, end);
  return sb.toString();
}

RString
Props::eval(IN(RString) str, short flags)
{
  RString erg = _eval(str, flags);
  erg = evalShellExecute(erg, flags);
  return removeQuoted(erg);
}

RString
Props::_eval(IN(RString) str, short flags)
{
  int start = 0;
  StringBuffer sb;
  RString inp = str;
  RString quotechar = getStringVal("CMDLINE_QUOTE_CHAR", flags | PropsNoWarnRead);

  if (quotechar->length() == 0)
    quotechar = "\"";
  bool changed = false;
  String::iterator it = inp->begin();
  String::iterator end = inp->end();
  String::iterator lstart = it;
  for (; it < end; ++it)
  {
    if ((it + 1) < end && *(it + 1) == '{' &&
        (*it == '$' || *it == '!' || *it == '@' || *it == '#'))
    {
      sb.append(lstart, it);
      char type = *it;
      it += 2;
      String::iterator ss = it;
      int openbrackets = 1;
      while (it < end)
      {
        ++it;
        if (*it == '}')
        {
          --openbrackets;
          if (openbrackets == 0)
            break;
        }
        if (*it == '{')
          ++openbrackets;
      }
      if (it == end)
      {
        sb.append(it, end);
        break;
      }
      RString iden = str->substr(ss - str->begin(), it - str->begin());

      if (type == '$')
      {
        RString nv;
        if (hasValue(iden, flags) == true)
        {
          nv = getStringVal(iden, flags);
        }
        else
        {
          RProps  cfgprops = new Props("", PropsParentRead | PropsParentWrite, this);
          RScript script = new Script("");
          cfgprops->setObjectVal("__script", &script, PropsNoParentWrite);
          RDmiObject o = script->evalExpr(iden, cfgprops, ScriptReadWriteParent);
          if (o != Nil)
            nv = o->toString();
        }
        if (nv != Nil)
        {
          if ((flags & PropsEvalQuoteFileNameArgs) && nv->indexOf(" ") != -1 && nv->indexOf(quotechar) == -1)
          {
            sb.append(quotechar);
            sb.append(nv);
            sb.append(quotechar);
          } else
            sb.append(nv);
          changed = true;
        } else
          ; // nothing?
      }
      else if (type == '@')
      {
        RStringArray sa = getStringArrayVal(iden, flags);
        for (int i = 0; i < sa->length(); ++i)
        {
          RString nv = sa[i];
          if (i > 0)
            sb.append(" ");
          if ((flags & PropsEvalQuoteFileNameArgs) && nv->indexOf(" ") != -1 && nv->indexOf(quotechar) == -1)
          {
            sb.append(quotechar);
            sb.append(nv);
            sb.append(quotechar);
          } else
            sb.append(nv);
        }
        changed = true;

      }
      else if (type == '!')
      {
        ++it;
        if (*it != '!')
        {
          sb.append(lstart, it);
          lstart = it;
          continue;
        }
        RProps cfgprops = new Props("", PropsParentRead | PropsParentWrite, this);
        acdk::io::RStringWriter out = new acdk::io::StringWriter();
        acdk::io::RPrintWriter pout = new acdk::io::PrintWriter(&out);
        cfgprops->setObjectVal("out", &pout, PropsNoParentWrite);
        RScript script = new Script("");
        cfgprops->setObjectVal("__script", &script, PropsNoParentWrite);
        if (script->eval(iden, &cfgprops, ScriptReadWriteParent) != 0)
        {
          sb.append(lstart, end);
          return sb.toString();
        }
        pout->flush();
        sb.append(out->getString());
        changed = true;
      }
      else if (type == '#')
      {
        ++it;
        sb.append(lstart, it + 1);
      }
      lstart = it + 1;
    }

  }
  if (changed == false)
    return inp;

  sb.append(lstart, end);


  inp = sb.toString();

  ACDK_NLOG("acdk.cfgscript", Debug, getName() + ": Props::eval: from [" + str + "] to [" + inp + "]");
  if ((flags & PropsEvalRecursive) == 0)
  {
    return inp;
  }
  return _eval(inp, flags);
}

void
Props::execScript(IN(RString) str, IN(RString) filename, short flags)
{
  RScript script = new Script(filename);
  script->initAsEnvProps(this);
  if (script->eval(str, this, ScriptReadParent | ScriptWriteParent) != 0)
  {
    // ### @todo handle error
  }
}

bool
Props::importNameSpace(IN(RString) prefix)
{
  return _importNameSpace(prefix, this);
}

bool
Props::_importNameSpace(IN(RString) prefix, IN(RProps) props)
{
  SYNCTHIS();
  bool ret = false;
  if (props->_parents != Nil)
  {
    for (int i = 0; i < props->_parents->length(); ++i)
      ret = _importNameSpace(prefix, props->_parents[i]);
  }

  RIterator it = props->_curHeap->keySet()->iterator();
  HashMap lhm;
  while (it->hasNext() == true)
  {
    RString k = (RString)it->next();
    if (k->startsWith(prefix + "_") == true)
    {
      acdk::lang::Object v = props->_curHeap->get(&k);
      RString nk = k->substr(prefix->length() + 1);
      ACDK_NLOG("acdk.cfgscript", Debug, getName() + ": Props::import: from " + k + " to " + nk + "=" + v);
      lhm.put(&nk, &v);
      ret = true;
    }
    else if (k->endsWith("_" + prefix) == true)
    {
      acdk::lang::Object v = props->_curHeap->get(&k);
      RString nk = k->substr(0, k->length() - (prefix->length() + 1));
      ACDK_NLOG("acdk.cfgscript", Debug, getName() + ": Props::import: from " + k + " to " + nk + "=" + v);
      lhm.put(&nk, &v);
      ret = true;
    }
    else
    {
      int f;
      if ((f = k->indexOf("_" + prefix + "_")) != -1)
      {
        RString nk = k->replace("_" + prefix + "_", "_");
        acdk::lang::Object v = props->_curHeap->get(&k);
        ACDK_NLOG("acdk.cfgscript", Debug, getName() + ": Props::import: from " + k + " to " + nk + "=" + v);
        lhm.put(&nk, &v);
        ret = true;
      }
    }
  }
  if (ret == true)
  {
    RIterator it = lhm.keySet()->iterator();
    while (it->hasNext() == true)
    {
      acdk::lang::Object k = it->next();
      acdk::lang::Object v = lhm.get(k);
      _curHeap->put(k, v);
    }
  }
  return ret;
}

void
Props::merge(IN(RProps) other, short flags)
{

  if ((flags & PropsMergeWithParent) && other->_parents != Nil)
  {
    for (int i = 0; i < other->_parents->length(); ++i)
      merge(other->_parents[i], flags);
  }
  SYNCTHIS();
  RIterator it = other->_curHeap->keySet()->iterator();
  while (it->hasNext() == true)
  {
    acdk::lang::Object key = it->next();
    RString skey = (RString)key;
    RDmiObject val = (RDmiObject)other->_curHeap->get(key);
    RDmiObject oldval = (RDmiObject)_curHeap->get(key);
    if (val != Nil && val->isObjectType() == true && instanceof(val->getObjectVar(), StringArray) == true)
    {
      if (oldval == Nil || ((PropsMergeOverWrite & flags) && ((PropsMergeAppendArrays & flags) != PropsMergeAppendArrays)))
      {
        ACDK_NLOG("acdk.cfgscript", Debug, "Merge: " + skey + "=" + val->toCode());
        set(skey, val, flags);
      }
      else if ((PropsMergeAppendArrays & flags) == PropsMergeAppendArrays)
      {
        if (oldval->isObjectType() == true && instanceof(oldval->getObjectVar(), StringArray) == true)
        {
          RStringArray sa = (RStringArray)oldval->getObjectVar();
          RStringArray na = (RStringArray)val->getObjectVar();
          for (int i = 0; i < na->length(); ++i)
          {
            sa->append(na[i]);
          }
          ACDK_NLOG("acdk.cfgscript", Debug, "Merge: " + skey + "=" + sa->toString());
          _set(skey, new DmiObject(inOf(sa)), flags);
        }
      }
    }
    else
    {
      if (oldval == Nil || PropsMergeOverWrite & flags)
      {
        ACDK_NLOG("acdk.cfgscript", Debug, "Merge: " + skey + "=" + val->toCode());
        _set(skey, val, flags);
      }
    }
  }
}


void
Props::_getAllStringVals(IN(RString) key, short flags, IN(RStringArray) values)
{
  RDmiObject p = get(&key, PropsNoParentRead | PropsWarnRead);
  if (p != Nil)
  {
    RString cv = p->getStringVar();//(RString)RDmiObject(_curHeap->get(&key))->getObjectVar();
    if (cv != Nil && containsString(values, cv) == false)
      values->append(cv);
  }
  if (_parents == Nil)
    return;
  for (int i = 0; i < _parents->length(); ++i)
    _parents[i]->_getAllStringVals(key, flags, values);
}

RStringArray
Props::getAllStringVals(IN(RString) key, short flags)
{
  RProps tp = this;
  RStringArray erg = new StringArray(0);
  _getAllStringVals(key, flags, erg);
  return erg;
}

void 
Props::addListener(IN(RPropsChangeListener) listener)
{
  if (_listeners == Nil)
    _listeners = new PropsChangeListenerArray(0);
  _listeners->append(listener);
}

void 
Props::removeListener(IN(RPropsChangeListener) listener)
{
  if (_listeners == Nil)
    return;
  for (int i  = 0; i < _listeners->length(); ++i)
  {
    if (_listeners[i] == listener)
    {
      _listeners->remove(i);
      return;
    }
  }
}

void
Props::_getKeys(INOUT(::acdk::util::TreeSet) keys, bool withParents)
{
  RIterator it = _curHeap->keySet()->iterator();
  while (it->hasNext() == true)
  {
    keys.add(it->next());
  }
  if (withParents == false)
    return;
  RPropsArray pa = getParentsProps();
  if (pa != Nil)
  {
    for (int i = 0; i < pa->length(); ++i)
    {
      pa[i]->_getKeys(keys);
    }
  }
}

void
Props::dump(int dumpFlags, ::acdk::util::RTreeSet usedkeys, IN(RString) ident)
{
  short flags = 0;

  ::acdk::util::TreeSet keys;

  _getKeys(keys, dumpFlags & DumpWithParent ? true : false);

  RIterator it = keys.iterator();
  while (it->hasNext() == true)
  {
    RString k = RString(it->next());
    RDmiObject v = get(k, flags);
    if (v->isStringType() == true)
    {
      System::out->println(ident + k + "=[\"" + v->toString() + "\"]");
    }
    else if (v->isObjectType() == true && instanceof(v->getObjectVar(), Props) == true)
    {
      if (dumpFlags & DumpWithChilds ||
          ((dumpFlags & DumpWithParent) && k->equals("_parent") == true))
      {
      if (usedkeys == Nil || k->equals("_parent") == true || usedkeys->contains(&k) == false)
      {
        if (usedkeys == Nil)
          usedkeys = new acdk::util::TreeSet();
        usedkeys->add(&k);
        System::out->println(ident + k + "=[");
        RProps(v->getObjectVar())->dump(dumpFlags, usedkeys, ident + "  ");
        System::out->println(ident + "]");
      }
      }
    }
    else
      System::out->println(ident + k + "=(" + v->getClass()->getName() + ")[" + v->toString() + "]");
  }
}


RString
Props::getAcdkHome(bool throwIfNotFound)
{
  RString erg = getStringVal("ACDKHOME", ScriptReadParent);
  if (erg != Nil && erg->length() > 0)
    return erg;
  erg = System::getAcdkHome();
  if (erg != Nil && erg->length() > 0)
    return erg;
  if (throwIfNotFound == true)
    THROW1(ScriptException, "Cannot find Property ACDKHOME or ACDK_HOME");
  return Nil;
}

RString
Props::getAcdkToolsHome(bool throwIfNotFound)
{
  RString erg = getStringVal("ACDK_TOOLS_HOME", PropsParentRead | PropsNoWarnRead);
  if (erg != Nil && erg->length() > 0)
    return erg;
  erg = System::getAcdkToolsHome();
  if (erg != Nil && erg->length() > 0)
    return erg;
  if (throwIfNotFound == true)
    THROW1(ScriptException, "Cannot find Property ACDK_TOOLS_HOME, ACDKHOME or ACDK_HOME");
  return Nil;
}

void 
Props::_asCsfLiteral(StringBuffer& sb, IN(RString) indent, short flags, IN(RString) key, IN(RDmiObject) val)
{
  if (val->isStringType() == true || val->isObjectType() == false)
  {
    sb << indent << "." << key << " = " + val->toCode() + ";\n";
    return;
  }
  if (val->isObjectType() == true)
  {
    acdk::lang::Object o = val->getObjectVar();
    if (instanceof(o, Props) == true)
    {
      RProps(o)->_asCsfLiteral(sb, "." + key, indent, flags);
      return;
    }
    if (instanceof(o, ObjectArray) == true)
    {
      // ### TODO support arrays
    }
    ACDK_NLOG("acdk.cfgscript", Error, "unsupported value type in render as CsfLiteral: " << o->getClass()->getName());
  }
}

void 
Props::_asCsfLiteral(StringBuffer& sb, IN(RString) keyName, IN(RString) indent, short flags)
{
  sb << indent << "with (" << keyName << " = new acdk.cfgscript.Props(\"" << keyName << "\"))\n" << indent << "{\n";
  RStringArray keys = getKeys(flags);
  for (int i = 0; i < keys->length(); ++i)
  {
    RString key = keys[i];
    _asCsfLiteral(sb, indent, flags, key, get(key));
  }
  sb << indent << "}\n";
}

void
Props_asLiteral(StringBuffer& sb, IN(acdk::lang::Object) val, IN(RString) indent)
{
  if (instanceof(val, Props) == true)
  {
    RProps p = (RProps)val;
    sb << "{\n";
    RString nindent = indent + "  ";
    RStringArray keys = p->getKeys(0);
    for (int i = 0; i < keys->length(); ++i)
    {
      RString key = keys[i];
      sb << nindent << key << ": ";
      Props_asLiteral(sb, &p->get(key), nindent);
      if (i != keys->length() - 1)
        sb << ",\n";
      else
        sb << "\n";
    }
    sb << indent << "}";
  }
  else if (instanceof(val, DmiObject) == true)
  {
    RDmiObject dval = (RDmiObject)val;
    if (dval->isStringType() == true || dval->isObjectType() == false)
    {
      sb << dval->toCode();
      return;
    }
    Props_asLiteral(sb, dval->getObjectVar(), indent);

  }
  else if (instanceof(val, DmiObjectArray) == true)
  {
    RDmiObjectArray oa = (RDmiObjectArray)val;
    sb << "[\n";
    RString nindent = indent + "  ";

    for (int i = 0; i < oa->length(); ++i)
    {
      sb << nindent;
      Props_asLiteral(sb, &oa[i], nindent);
      if (i != oa->length() - 1)
        sb << ",\n";
      else
        sb << "\n";
    }
    sb << indent << "]";
  }
  else
  {
    ACDK_NLOG("acdk.cfgscript", Error, "unsupported value type in render as CsfLiteral: " << val->getClass()->getName());
  }
}

RString 
Props::asCfgScriptLiteral(IN(RString) keyName, IN(RString) indent, short flags)
{
  StringBuffer sb;
  Props_asLiteral(sb, this, "");
  //_asCsfLiteral(sb, keyName, indent + "  ", flags);
  return sb.toString();
}

const acdk::lang::dmi::ClazzMethodInfo*
Props::standardDispatch(  IN(acdk::lang::RString) fname, acdk::lang::dmi::ScriptVar& ret,
                                acdk::lang::dmi::ScriptVarArray& args,
                                acdk::lang::dmi::DmiClient& dc,
                                IN(::acdk::lang::RStringArray) namedArgs/* = Nil*/,
                                int flags,
                                const acdk::lang::dmi::ClazzInfo* clazzinfo,
                                const acdk::lang::dmi::ClazzMethodInfo* methinf/* = 0*/)
{
  ASCLITERAL(peek);
  ASCLITERAL(poke);
  if (fname->equals(lit_peek) == true)
  {
    RString vn = args[0].getStringVar();
    RDmiObject obj = get(vn);
    if (obj == Nil)
      ret = Nil;
    else
      ret = *obj;
    return (const acdk::lang::dmi::ClazzMethodInfo*)1;
  }
  else if (fname->equals(lit_poke) == true)
  {
    RString vn = args[0].getStringVar();
    set(vn, new DmiObject(args[1]));
    return (const acdk::lang::dmi::ClazzMethodInfo*)1;
  }
  else
    return ACDK_FQ_SUPER_QUALIFIER(acdk::lang::dmi::, StdDispatch::)standardDispatch(fname, ret, args, dc, namedArgs, flags, clazzinfo, methinf);
}

} // namespace cfgscript
} // namespace acdk