/** Get a named attribute of the variable; e.g. xxx.yyy */
 @Override
 public VariableReference getAttribute(String attributeName) throws ScriptException {
   // We recognize the __size__ attribute
   if (attributeName.equals(ATTRIBUTE_SIZE))
     return new VariableInt(configurationNode.getChildCount());
   // Also, the __type__ attribute
   if (attributeName.equals(ATTRIBUTE_TYPE))
     return new VariableString(configurationNode.getType());
   // And the __value__ attribute
   if (attributeName.equals(ATTRIBUTE_VALUE)) return new ValueReference();
   if (attributeName.equals(ATTRIBUTE_DICT)) {
     VariableDict dict = new VariableDict();
     int i = 0;
     while (i < configurationNode.getChildCount()) {
       ConfigurationNode child = configurationNode.findChild(i++);
       String type = child.getType();
       dict.getIndexed(new VariableString(type))
           .setReference(new VariableConfigurationNode(child));
     }
     return dict;
   }
   if (attributeName.equals(ATTRIBUTE_SCRIPT)
       || attributeName.equals(ATTRIBUTE_STRING)
       || attributeName.equals(ATTRIBUTE_INT)
       || attributeName.equals(ATTRIBUTE_FLOAT)
       || attributeName.equals(ATTRIBUTE_BOOLEAN)) return super.getAttribute(attributeName);
   // All others are presumed to be attributes of the configuration node, which can be set or
   // cleared.
   return new AttributeReference(attributeName);
 }
 /** Insert an object into this variable at a position. */
 @Override
 public void insertAt(Variable v, Variable index) throws ScriptException {
   if (v == null) throw new ScriptException(composeMessage("Can't insert a null object"));
   if (index == null)
     configurationNode.addChild(configurationNode.getChildCount(), v.getConfigurationNodeValue());
   else {
     int indexValue = index.getIntValue();
     if (indexValue < 0 || indexValue > configurationNode.getChildCount())
       throw new ScriptException(composeMessage("Insert out of bounds: " + indexValue));
     configurationNode.addChild(indexValue, v.getConfigurationNodeValue());
   }
 }
 /** Delete an object from this variable at a position. */
 @Override
 public void removeAt(Variable index) throws ScriptException {
   if (index == null) throw new ScriptException(composeMessage("Remove index cannot be null"));
   int indexValue = index.getIntValue();
   if (indexValue < 0 || indexValue >= configurationNode.getChildCount())
     throw new ScriptException(composeMessage("Remove index out of bounds: " + indexValue));
   configurationNode.removeChild(indexValue);
 }
 /** Get an indexed property of the variable */
 @Override
 public VariableReference getIndexed(Variable index) throws ScriptException {
   if (index == null) throw new ScriptException(composeMessage("Subscript cannot be null"));
   int indexValue = index.getIntValue();
   if (indexValue >= 0 && indexValue < configurationNode.getChildCount())
     return new NodeReference(indexValue);
   throw new ScriptException(composeMessage("Subscript is out of bounds: " + indexValue));
 }
 @Override
 public VariableReference plus(Variable v) throws ScriptException {
   if (v == null) throw new ScriptException(composeMessage("Can't add a null object"));
   ConfigurationNode node = v.getConfigurationNodeValue();
   ConfigurationNode cn = new ConfigurationNode(configurationNode.getType());
   cn.setValue(configurationNode.getValue());
   Iterator<String> attIter = configurationNode.getAttributes();
   while (attIter.hasNext()) {
     String attrName = attIter.next();
     String attrValue = configurationNode.getAttributeValue(attrName);
     cn.setAttribute(attrName, attrValue);
   }
   int i = 0;
   while (i < configurationNode.getChildCount()) {
     ConfigurationNode child = configurationNode.findChild(i++);
     cn.addChild(cn.getChildCount(), child);
   }
   cn.addChild(cn.getChildCount(), node);
   return new VariableConfigurationNode(cn);
 }
 /** Get the variable's script value */
 @Override
 public String getScriptValue() throws ScriptException {
   StringBuilder sb = new StringBuilder();
   sb.append("<< ");
   sb.append(new VariableString(configurationNode.getType()).getScriptValue());
   sb.append(" : ");
   String valueField = configurationNode.getValue();
   if (valueField == null) valueField = "";
   sb.append(new VariableString(valueField).getScriptValue());
   sb.append(" : ");
   boolean needComma = false;
   Iterator<String> iter = configurationNode.getAttributes();
   String[] attrs = new String[configurationNode.getAttributeCount()];
   int i = 0;
   while (iter.hasNext()) {
     String attrName = iter.next();
     attrs[i++] = attrName;
   }
   java.util.Arrays.sort(attrs);
   i = 0;
   while (i < attrs.length) {
     String attrName = attrs[i++];
     String value = configurationNode.getAttributeValue(attrName);
     if (needComma) sb.append(", ");
     else needComma = true;
     sb.append(new VariableString(attrName).getScriptValue());
     sb.append("=");
     sb.append(new VariableString(value).getScriptValue());
   }
   sb.append(" : ");
   i = 0;
   while (i < configurationNode.getChildCount()) {
     ConfigurationNode child = configurationNode.findChild(i);
     if (i > 0) sb.append(", ");
     sb.append(new VariableConfigurationNode(child).getScriptValue());
     i++;
   }
   sb.append(" >>");
   return sb.toString();
 }