producer XML configuration support: Producers can be set up using an XML input
authorzapata <zapata>
Sun, 18 Aug 2002 15:29:41 +0000 (15:29 +0000)
committerzapata <zapata>
Sun, 18 Aug 2002 15:29:41 +0000 (15:29 +0000)
file.

71 files changed:
bundles/admin_en.properties
source/config.properties-dist
source/mir/generator/CompositeGeneratorLibrary.java [new file with mode: 0755]
source/mir/generator/FreemarkerGenerator.java
source/mir/generator/Generator.java
source/mir/generator/GeneratorExc.java [new file with mode: 0755]
source/mir/generator/GeneratorException.java [deleted file]
source/mir/generator/GeneratorFailure.java [new file with mode: 0755]
source/mir/generator/WriterEngine.java [new file with mode: 0755]
source/mir/producer/AssignmentProducerNode.java
source/mir/producer/CompositeProducerNode.java
source/mir/producer/ConditionalProducerNode.java [new file with mode: 0755]
source/mir/producer/EntityBatchingProducerNode.java
source/mir/producer/EntityEnumeratingProducerNode.java
source/mir/producer/EntityListProducerNode.java
source/mir/producer/EvaluatedAssignmentProducerNode.java
source/mir/producer/ExpandedAssignmentProducerNode.java [new file with mode: 0755]
source/mir/producer/GeneratingProducerNode.java [new file with mode: 0755]
source/mir/producer/LoggingProducerNode.java [new file with mode: 0755]
source/mir/producer/MediaProducerNode.java [deleted file]
source/mir/producer/NodedProducer.java
source/mir/producer/NodedProducerFactory.java [new file with mode: 0755]
source/mir/producer/Producer.java
source/mir/producer/ProducerExc.java [new file with mode: 0755]
source/mir/producer/ProducerFactory.java
source/mir/producer/ProducerFailure.java
source/mir/producer/ProducerNode.java
source/mir/producer/ProducerNodeDecorator.java
source/mir/producer/ResourceBundleProducerNode.java
source/mir/producer/reader/DefaultProducerNodeBuilders.java [new file with mode: 0755]
source/mir/producer/reader/ProducerConfigExc.java [new file with mode: 0755]
source/mir/producer/reader/ProducerConfigFailure.java [new file with mode: 0755]
source/mir/producer/reader/ProducerConfigReader.java [new file with mode: 0755]
source/mir/producer/reader/ProducerNodeBuilder.java [new file with mode: 0755]
source/mir/producer/reader/ProducerNodeBuilderLibrary.java [new file with mode: 0755]
source/mir/producer/reader/ReaderTool.java [new file with mode: 0755]
source/mir/producer/reader/ScriptedProducerFactory.java [new file with mode: 0755]
source/mir/producer/reader/ScriptedProducerNode.java [new file with mode: 0755]
source/mir/producer/reader/ScriptedProducerNodeDefinition.java [new file with mode: 0755]
source/mir/producer/reader/ScriptedProducerNodeTool.java [new file with mode: 0755]
source/mir/util/FileMonitor.java [new file with mode: 0755]
source/mir/util/ParameterExpander.java
source/mircoders/global/ProducerEngine.java
source/mircoders/localizer/MirCachingLocalizerDecorator.java
source/mircoders/localizer/MirGeneratorLocalizer.java
source/mircoders/localizer/MirLocalizer.java
source/mircoders/localizer/MirLocalizerFailure.java
source/mircoders/localizer/MirProducerAssistantLocalizer.java
source/mircoders/localizer/basic/MirBasicGeneratorLocalizer.java
source/mircoders/localizer/basic/MirBasicLocalizer.java
source/mircoders/localizer/basic/MirBasicProducerAssistantLocalizer.java
source/mircoders/localizer/basic/MirBasicProducerLocalizer.java
source/mircoders/localizer/basic/MirBasicWriterEngine.java [new file with mode: 0755]
source/mircoders/producer/CompositeProducer.java
source/mircoders/producer/CompositeProducerFactory.java
source/mircoders/producer/GeneratingProducerNode.java [deleted file]
source/mircoders/producer/IndexingProducerNode.java
source/mircoders/producer/NodedProducerFactory.java [deleted file]
source/mircoders/producer/OldProducerAdapterFactory.java
source/mircoders/producer/PDFGeneratingProducerNode.java
source/mircoders/producer/reader/SupplementalProducerNodeBuilders.java [new file with mode: 0755]
source/mircoders/servlet/ServletModuleOpenIndy.java
source/mirlocal/bolivia.indymedia.org/BoliviaLocalizer.java
source/mirlocal/bolivia.indymedia.org/BoliviaProducerLocalizer.java
source/mirlocal/ecuador.indymedia.org/EcuadorLocalizer.java
source/mirlocal/ecuador.indymedia.org/EcuadorProducerLocalizer.java
source/mirlocal/euskalherria.indymedia.org/EHLocalizer.java
source/mirlocal/euskalherria.indymedia.org/EHProducerLocalizer.java
source/mirlocal/indymedia.nl/IndyNLLocalizer.java
source/mirlocal/indymedia.nl/IndyNLProducerLocalizer.java
templates-dist/open/posting.template

index 0b4a69e..a136dc9 100755 (executable)
@@ -275,27 +275,6 @@ usererror.text=Your input caused the following error:
 usererror.what_to_do=Please press the back button and try it again
 
 
-########## producer ##########
-
-producer.content.htmltitle=mir.indymedia.de:
-producer.content.email=eMail
-producer.content.homepage=Homepage
-producer.content.comment=Make a quick comment on this article
-
-producer.copyright=Jegliche Inhalte, die bei germany.indymedia ver&ouml;ffentlicht werden, bleiben Eigentum der Autorin/ des Autors. Soweit nicht anders vermerkt, k&ouml;nnen und sollen sie weiterverwertet werden. germany.indymedia &uuml;bernimmt keine Gew&auml;hr f&uuml;r die Inhalte.<br>Eine spezielle  Form des Copyrights wird diskutiert und folgt.
-
-producer.contact=Kontakt
-
-producer.openposting.htmltitle=mir.indymedia.de:
-
-producer.startpage.htmltitle=mir.indymedia.de: the mir-coders-website
-
-producer.topiclist.htmltitle=mir.indymedia.de:
-
-producer.previous=previous page
-producer.next=next page
-
-
 ########## open ##########
 
 open.optional=optional
index 7bcb1c9..a7ea86e 100755 (executable)
@@ -40,7 +40,11 @@ ClearXslCache=no
 StandardLanguage=de
 DirectOpenposting=yes
 
+Mir.Localizer=mircoders.localizer.basic.MirBasicLocalizer
 Mir.Localizer.Logfile=log/localizer.log
+Mir.Localizer.ProducerConfigFile=templates/producer/producers.xml
+
+
 
 #note that you can't make pdf's without making fo's
 GenerateFO=yes
diff --git a/source/mir/generator/CompositeGeneratorLibrary.java b/source/mir/generator/CompositeGeneratorLibrary.java
new file mode 100755 (executable)
index 0000000..2c1a949
--- /dev/null
@@ -0,0 +1,46 @@
+package mir.generator;
+
+import java.util.*;
+
+public class CompositeGeneratorLibrary implements Generator.GeneratorLibrary {
+  private Map generatorLibraries;
+  private Generator.GeneratorLibrary defaultLibrary = null;
+  private static String LIBRARY_QUALIFIER_SEPARATOR = "::";
+
+  public CompositeGeneratorLibrary() {
+    generatorLibraries = new HashMap();
+  }
+
+  public void addLibrary(String aQualifier, Generator.GeneratorLibrary aLibrary, boolean anIsDefault) {
+    if (anIsDefault || defaultLibrary == null) {
+      defaultLibrary = aLibrary;
+    }
+
+    generatorLibraries.put(aQualifier, aLibrary);
+  }
+
+  public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {
+    String qualifier;
+    String libraryName;
+    int position;
+    Generator.GeneratorLibrary library;
+
+    position = anIdentifier.indexOf( LIBRARY_QUALIFIER_SEPARATOR );
+    if (position>=0) {
+      libraryName = anIdentifier.substring(0, position);
+      qualifier = anIdentifier.substring(position + LIBRARY_QUALIFIER_SEPARATOR.length());
+
+      library = (Generator.GeneratorLibrary) generatorLibraries.get(libraryName);
+      if (library==null)
+        throw new GeneratorExc("CompositeGeneratorLibrary: library '"+libraryName+"' not found");
+
+      return library.makeGenerator(qualifier);
+    }
+    else {
+      if (defaultLibrary!=null)
+        return defaultLibrary.makeGenerator(anIdentifier);
+      else
+        throw new GeneratorExc("CompositeGeneratorLibrary: no default library speficied");
+    }
+  };
+}
\ No newline at end of file
index 15147c8..bcb4020 100755 (executable)
@@ -1,12 +1,12 @@
 package mir.generator;
 
+import freemarker.template.*;
+import org.apache.struts.util.MessageResources;
 import java.util.*;
 import java.io.*;
-import freemarker.template.*;
 import mir.entity.*;
 import mir.util.*;
 import mir.misc.*;
-import org.apache.struts.util.MessageResources;
 
 public class FreemarkerGenerator implements Generator {
   private Template template;
@@ -15,15 +15,19 @@ public class FreemarkerGenerator implements Generator {
     template = aTemplate;
   }
 
-  public void generate(PrintWriter anOutputWriter, Map aValues, PrintWriter aLogger) throws GeneratorException {
+  public void generate(Object anOutputWriter, Map aValues, PrintWriter aLogger) throws GeneratorExc, GeneratorFailure {
+    if (!(anOutputWriter instanceof PrintWriter))
+      throw new GeneratorExc("Writer for a FreemarkerGenerator must be a PrintWriter");
+
     try {
-                 template.process((TemplateModelRoot) makeMapAdapter(aValues), anOutputWriter);
+      template.process((TemplateModelRoot) makeMapAdapter(aValues), (PrintWriter) anOutputWriter);
     }
     catch (Throwable t) {
       aLogger.println("Exception occurred: "+t.getMessage());
       t.printStackTrace(aLogger);
+      throw new GeneratorFailure( t );
     }
-       }
+  }
 
   private static TemplateScalarModel makeStringAdapter(String aString) {
     return new SimpleScalar(aString);
@@ -37,110 +41,110 @@ public class FreemarkerGenerator implements Generator {
     return new IteratorAdapter(anIterator);
   }
 
-       private static TemplateModel makeAdapter(Object anObject) throws TemplateModelException {
-         if (anObject == null)
-           return null;
-         if (anObject instanceof TemplateModel)
-           return (TemplateModel) anObject;
-         else if (anObject instanceof MessageResources)
-           return new MessageMethodModel((MessageResources) anObject);
-         else if (anObject instanceof String)
-           return makeStringAdapter((String) anObject);
-         else if (anObject instanceof Map)
-           return makeMapAdapter((Map) anObject);
-         else if (anObject instanceof Iterator)
-           return makeIteratorAdapter((Iterator) anObject);
-         else if (anObject instanceof List)
-           return makeIteratorAdapter(((List) anObject).iterator());
-         else
-           throw new TemplateModelException("Unadaptable class: " + anObject.getClass().getName());
-       }
-
-       private static class MapAdapter implements TemplateModelRoot {
-         Map map;
-         Map valuesCache;
-
-         private MapAdapter(Map aMap) {
-           map = aMap;
-           valuesCache = new HashMap();
-         }
-
-         public void put(String aKey, TemplateModel aModel) {
-           valuesCache.put(aKey, aModel);
-         }
-
-         public void remove(String aKey) {
-           // ML: kinda tricky...
-         }
-
-         public boolean isEmpty() {
-           return map.isEmpty();
-         }
-
-         public TemplateModel get(String aKey) throws TemplateModelException {
-           try {
-           if (!valuesCache.containsKey(aKey)) {
-             Object value = map.get(aKey);
-
-        if (value == null && !map.containsKey(aKey))
-            throw new TemplateModelException("MapAdapter: no key "+aKey+" available");
-
-             valuesCache.put(aKey, makeAdapter(value));
-           }
-
-           return (TemplateModel) valuesCache.get(aKey);
-         }
-         catch (TemplateModelException e) {
-           throw e;
-         }
-         catch (Throwable t) {
-           throw new TemplateModelException(t.getMessage());
-         }
-         }
-       }
-
-       private static class IteratorAdapter implements TemplateListModel {
-         Iterator iterator;
-         List valuesCache;
-         int position;
-
-         private IteratorAdapter(Iterator anIterator) {
-           iterator = anIterator;
-
-           valuesCache = new Vector();
-           position=0;
-
-
-           if (iterator instanceof RewindableIterator) {
-             ((RewindableIterator) iterator).rewind();
-           }
-         }
-
-         public boolean isEmpty() {
-           return valuesCache.isEmpty() && !iterator.hasNext();
-         }
-
-         private void getUntil(int anIndex) throws TemplateModelException {
-           while (valuesCache.size()<=anIndex && iterator.hasNext())
-           {
-             valuesCache.add(makeAdapter(iterator.next()));
-           }
-         };
-
-         public TemplateModel get(int anIndex) throws TemplateModelException {
-           TemplateModel result;
-
-           getUntil(anIndex);
-
-           if (anIndex<valuesCache.size())
-           {
-             result = (TemplateModel) valuesCache.get(anIndex);
-
-             return result;
-           }
-           else
-             throw new TemplateModelException( "Iterator out of bounds" );
-         }
+  private static TemplateModel makeAdapter(Object anObject) throws TemplateModelException {
+    if (anObject == null)
+      return null;
+    if (anObject instanceof TemplateModel)
+      return (TemplateModel) anObject;
+    else if (anObject instanceof MessageResources)
+      return new MessageMethodModel((MessageResources) anObject);
+    else if (anObject instanceof String)
+      return makeStringAdapter((String) anObject);
+    else if (anObject instanceof Map)
+      return makeMapAdapter((Map) anObject);
+    else if (anObject instanceof Iterator)
+      return makeIteratorAdapter((Iterator) anObject);
+    else if (anObject instanceof List)
+      return makeIteratorAdapter(((List) anObject).iterator());
+    else
+      throw new TemplateModelException("Unadaptable class: " + anObject.getClass().getName());
+  }
+
+  private static class MapAdapter implements TemplateModelRoot {
+    Map map;
+    Map valuesCache;
+
+    private MapAdapter(Map aMap) {
+      map = aMap;
+      valuesCache = new HashMap();
+    }
+
+    public void put(String aKey, TemplateModel aModel) {
+      valuesCache.put(aKey, aModel);
+    }
+
+    public void remove(String aKey) {
+      // ML: kinda tricky...
+    }
+
+    public boolean isEmpty() {
+      return map.isEmpty();
+    }
+
+    public TemplateModel get(String aKey) throws TemplateModelException {
+      try {
+      if (!valuesCache.containsKey(aKey)) {
+        Object value = map.get(aKey);
+
+  if (value == null && !map.containsKey(aKey))
+      throw new TemplateModelException("MapAdapter: no key "+aKey+" available");
+
+        valuesCache.put(aKey, makeAdapter(value));
+      }
+
+      return (TemplateModel) valuesCache.get(aKey);
+    }
+    catch (TemplateModelException e) {
+      throw e;
+    }
+    catch (Throwable t) {
+      throw new TemplateModelException(t.getMessage());
+    }
+    }
+  }
+
+  private static class IteratorAdapter implements TemplateListModel {
+    Iterator iterator;
+    List valuesCache;
+    int position;
+
+    private IteratorAdapter(Iterator anIterator) {
+      iterator = anIterator;
+
+      valuesCache = new Vector();
+      position=0;
+
+
+      if (iterator instanceof RewindableIterator) {
+        ((RewindableIterator) iterator).rewind();
+      }
+    }
+
+    public boolean isEmpty() {
+      return valuesCache.isEmpty() && !iterator.hasNext();
+    }
+
+    private void getUntil(int anIndex) throws TemplateModelException {
+      while (valuesCache.size()<=anIndex && iterator.hasNext())
+      {
+        valuesCache.add(makeAdapter(iterator.next()));
+      }
+    };
+
+    public TemplateModel get(int anIndex) throws TemplateModelException {
+      TemplateModel result;
+
+      getUntil(anIndex);
+
+      if (anIndex<valuesCache.size())
+      {
+        result = (TemplateModel) valuesCache.get(anIndex);
+
+        return result;
+      }
+      else
+        throw new TemplateModelException( "Iterator out of bounds" );
+    }
 
     public boolean hasNext() {
       return position<valuesCache.size() || iterator.hasNext();
@@ -158,7 +162,7 @@ public class FreemarkerGenerator implements Generator {
         position++;
       }
       else
-             throw new TemplateModelException( "Iterator out of bounds" );
+              throw new TemplateModelException( "Iterator out of bounds" );
 
       return result;
     }
@@ -166,36 +170,36 @@ public class FreemarkerGenerator implements Generator {
     public void rewind() {
       position=0;
     }
-       }
+  }
 
-       private static class ListAdapter implements TemplateListModel {
-         List list;
-         List valuesCache;
-         int position;
+  private static class ListAdapter implements TemplateListModel {
+    List list;
+    List valuesCache;
+    int position;
 
-         private ListAdapter(List aList) {
-           list = aList;
-           valuesCache = new Vector();
-           position=0;
-         }
+    private ListAdapter(List aList) {
+      list = aList;
+      valuesCache = new Vector();
+      position=0;
+    }
 
-         public boolean isEmpty() {
-           return list.isEmpty();
-         }
+    public boolean isEmpty() {
+      return list.isEmpty();
+    }
 
-         public TemplateModel get(int i) throws TemplateModelException {
+    public TemplateModel get(int i) throws TemplateModelException {
 
-           if (i>=valuesCache.size() && i<list.size()) {
-             for(int j=valuesCache.size(); j<=i; j++) {
-               valuesCache.add(makeAdapter(list.get(j)));
-             }
-           }
+      if (i>=valuesCache.size() && i<list.size()) {
+        for(int j=valuesCache.size(); j<=i; j++) {
+          valuesCache.add(makeAdapter(list.get(j)));
+        }
+      }
 
-           if (i<valuesCache.size())
-             return (TemplateModel) valuesCache.get(i);
-           else
-             throw new TemplateModelException( "Iterator out of bounds" );
-         }
+      if (i<valuesCache.size())
+        return (TemplateModel) valuesCache.get(i);
+      else
+        throw new TemplateModelException( "Iterator out of bounds" );
+    }
 
     public boolean hasNext() {
       return position<list.size();
@@ -212,14 +216,34 @@ public class FreemarkerGenerator implements Generator {
         result = get(position);
         position++;
       }
-           else
-             throw new TemplateModelException( "Iterator out of bounds" );
+      else {
+        throw new TemplateModelException( "Iterator out of bounds" );
+      }
 
       return result;
     }
 
     public void rewind() {
-      position=0;
+      position = 0;
+    }
+  }
+
+  public static class FreemarkerGeneratorLibrary implements GeneratorLibrary {
+    private FileTemplateCache  templateCache;
+
+    public FreemarkerGeneratorLibrary(String aTemplateRoot) {
+      templateCache = new FileTemplateCache( aTemplateRoot + "/" );
+      templateCache.setLoadingPolicy(templateCache.LOAD_ON_DEMAND);
+    }
+
+    public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {
+      Template template = (Template) templateCache.getItem(anIdentifier, "template");
+
+      if (template==null) {
+        throw new GeneratorExc("FreemarkerGeneratorLibrary: Can't find template "+templateCache.getDirectory()+anIdentifier);
+      }
+
+      return new FreemarkerGenerator(template);
     }
-       }
+  }
 }
index a3dd592..9a546d7 100755 (executable)
@@ -4,5 +4,9 @@ import java.util.*;
 import java.io.*;
 
 public interface Generator {
-  public void generate(PrintWriter anOutputWriter, Map aValues, PrintWriter aLogger) throws GeneratorException;
+  public void generate(Object anOutputWriter, Map aValues, PrintWriter aLogger) throws GeneratorExc, GeneratorFailure;
+
+  public static interface GeneratorLibrary {
+    public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure;
+  }
 }
diff --git a/source/mir/generator/GeneratorExc.java b/source/mir/generator/GeneratorExc.java
new file mode 100755 (executable)
index 0000000..94cdcf9
--- /dev/null
@@ -0,0 +1,9 @@
+package mir.generator;
+
+import multex.Exc;
+
+public class GeneratorExc extends Exc {
+  public GeneratorExc(String aMessage) {
+    super(aMessage);
+  }
+}
diff --git a/source/mir/generator/GeneratorException.java b/source/mir/generator/GeneratorException.java
deleted file mode 100755 (executable)
index 57fcdad..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-package mir.generator;
-
-public class GeneratorException extends Exception {
-}
\ No newline at end of file
diff --git a/source/mir/generator/GeneratorFailure.java b/source/mir/generator/GeneratorFailure.java
new file mode 100755 (executable)
index 0000000..a729cd5
--- /dev/null
@@ -0,0 +1,14 @@
+package mir.generator;
+
+import multex.Failure;
+
+public class GeneratorFailure extends Failure {
+
+    public GeneratorFailure(String aMessage, Throwable aCause) {
+      super(aMessage, aCause);
+    }
+
+    public GeneratorFailure(Throwable aCause) {
+      this (aCause.getMessage(), aCause);
+    }
+}
\ No newline at end of file
diff --git a/source/mir/generator/WriterEngine.java b/source/mir/generator/WriterEngine.java
new file mode 100755 (executable)
index 0000000..728b2d2
--- /dev/null
@@ -0,0 +1,15 @@
+package mir.generator;
+
+/**
+ * <p>Title: </p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2002</p>
+ * <p>Company: </p>
+ * @author unascribed
+ * @version 1.0
+ */
+
+public interface WriterEngine {
+  public Object openWriter(String anIdentifier, String aParameter) throws GeneratorExc, GeneratorFailure;
+  public void closeWriter(Object aWriter) throws GeneratorExc, GeneratorFailure;
+}
\ No newline at end of file
index 4537ef8..1c13cac 100755 (executable)
@@ -18,21 +18,13 @@ public class AssignmentProducerNode extends ProducerNodeDecorator {
   }
 
   public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure {
-    Object savedValue;
-
-    savedValue = aValueMap.get(key);
     try {
-      try {
-        aValueMap.put(key, value);
+      ParameterExpander.setValueForKey(aValueMap, key, value);
 
-        super.produce(aValueMap, aVerb, aLogger);
-      }
-      catch (Throwable t) {
-        throw new ProducerFailure(t.getMessage(), t);
-      }
+      super.produce(aValueMap, aVerb, aLogger);
     }
-    finally {
-      aValueMap.put(key,savedValue);
+    catch (Throwable t) {
+      throw new ProducerFailure(t.getMessage(), t);
     }
   };
 
index 0ce4962..503a87d 100755 (executable)
@@ -20,19 +20,30 @@ public class CompositeProducerNode implements ProducerNode {
     }
   }
 
+  public int getNrSubNodes() {
+    return subNodes.size();
+  }
+
+  public ProducerNode getSubNode(int anIndex) {
+    return (ProducerNode) subNodes.get(anIndex);
+  }
+
   public void addSubNode(ProducerNode aSubNode) {
-    subNodes.add(aSubNode);
+    if (aSubNode!=null)
+      subNodes.add(aSubNode);
   }
 
   public void clear() {
     subNodes.clear();
   }
 
-  public void produce(Map aValueSet, String aVerb, PrintWriter aLogger) throws ProducerFailure {
+  public void produce(Map aValueSet, String aVerb, PrintWriter aLogger) throws ProducerFailure, ProducerExc {
     Iterator i = subNodes.iterator();
 
-    while (i.hasNext())
-      ((ProducerNode) i.next()).produce(aValueSet, aVerb, aLogger);
+    while (i.hasNext()) {
+      ProducerNode node = (ProducerNode) i.next();
+      node.produce(aValueSet, aVerb, aLogger);
+    }
   }
 
   public Set buildVerbSet() {
diff --git a/source/mir/producer/ConditionalProducerNode.java b/source/mir/producer/ConditionalProducerNode.java
new file mode 100755 (executable)
index 0000000..8642b2b
--- /dev/null
@@ -0,0 +1,37 @@
+package mir.producer;
+
+import java.util.*;
+import java.io.*;
+import mir.util.*;
+
+public class ConditionalProducerNode implements ProducerNode {
+  private String condition;
+  private ProducerNode trueNode;
+  private ProducerNode falseNode;
+
+  public ConditionalProducerNode(String aCondition, ProducerNode aTrueNode, ProducerNode aFalseNode) {
+    condition = aCondition;
+    trueNode = aTrueNode;
+    falseNode = aFalseNode;
+  }
+
+  public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure {
+    try {
+      if (ParameterExpander.evaluateBooleanExpression(aValueMap, condition)) {
+        if (trueNode!=null)
+          trueNode.produce(aValueMap, aVerb, aLogger);
+      }
+      else {
+        if (falseNode!=null)
+          falseNode.produce(aValueMap, aVerb, aLogger);
+      }
+    }
+    catch (Exception e) {
+      throw new ProducerFailure(e);
+    }
+  }
+
+  public Set buildVerbSet() {
+    return new HashSet();
+  }
+}
\ No newline at end of file
index 5f3b2bf..ce256ff 100755 (executable)
@@ -122,7 +122,7 @@ public class EntityBatchingProducerNode implements ProducerNode {
       }
 
       batchData = new HashMap();
-      aValueMap.put(batchInfoKey, batchData);
+      ParameterExpander.setValueForKey(aValueMap, batchInfoKey, batchData);
       batchData.put("all", batchesData);
       batchData.put("first", batchesData.get(0));
       batchData.put("last", batchesData.get(batchesData.size()-1));
@@ -159,7 +159,7 @@ public class EntityBatchingProducerNode implements ProducerNode {
           while (j.hasNext())
             entities.add(0, j.next());
 
-          aValueMap.put( batchDataKey, entities );
+          ParameterExpander.setValueForKey(aValueMap, batchDataKey, entities );
 
           batchSubNode.produce(aValueMap, aVerb, aLogger);
         }
index aaca592..cbf5383 100755 (executable)
@@ -57,7 +57,7 @@ public class EntityEnumeratingProducerNode extends ProducerNodeDecorator {
           definition );
 
       while (browser.hasNext()) {
-        aValueMap.put(key, browser.next());
+        ParameterExpander.setValueForKey( aValueMap, key, browser.next());
         super.produce(aValueMap, aVerb, aLogger);
       }
     }
index ed7fe37..28f00b5 100755 (executable)
@@ -34,7 +34,9 @@ public class EntityListProducerNode extends ProducerNodeDecorator {
 
   public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure {
     try {
-      aValueMap.put(key,
+      ParameterExpander.setValueForKey(
+        aValueMap,
+        key,
         new CachingRewindableIterator(
           new EntityIteratorAdapter(
             ParameterExpander.expandExpression( aValueMap, whereClause ),
index c9af95b..0a7d071 100755 (executable)
@@ -2,38 +2,32 @@ package mir.producer;
 
 import java.util.*;
 import java.io.*;
-import org.apache.struts.util.MessageResources;
 import mir.util.*;
 
-public class EvaluatedAssignmentProducerNode extends ProducerNodeDecorator {
+public class EvaluatedAssignmentProducerNode implements ProducerNode {
   private String key;
   private String bundleIdentifier;
   private String value;
 
-  public EvaluatedAssignmentProducerNode(String aKey, String aValue, ProducerNode aSubNode) {
-    super(aSubNode);
-
+  public EvaluatedAssignmentProducerNode(String aKey, String aValue) {
     key = aKey;
     value = aValue;
   }
 
   public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure {
-    Object savedValue;
-
-    savedValue = aValueMap.get(key);
     try {
-      try {
-        aValueMap.put(key, ParameterExpander.expandExpression( aValueMap, value ));
+      ParameterExpander.setValueForKey(
+         aValueMap,
+         key,
+         ParameterExpander.evaluateExpression( aValueMap, value ));
 
-        super.produce(aValueMap, aVerb, aLogger);
-      }
-      catch (Throwable t) {
-        throw new ProducerFailure(t.getMessage(), t);
-      }
     }
-    finally {
-      aValueMap.put(key,savedValue);
+    catch (Throwable t) {
+      throw new ProducerFailure(t.getMessage(), t);
     }
   };
 
+  public Set buildVerbSet() {
+    return new HashSet();
+  }
 }
\ No newline at end of file
diff --git a/source/mir/producer/ExpandedAssignmentProducerNode.java b/source/mir/producer/ExpandedAssignmentProducerNode.java
new file mode 100755 (executable)
index 0000000..86d1012
--- /dev/null
@@ -0,0 +1,33 @@
+package mir.producer;
+
+import java.util.*;
+import java.io.*;
+import mir.util.*;
+
+public class ExpandedAssignmentProducerNode implements ProducerNode {
+  private String key;
+  private String bundleIdentifier;
+  private String value;
+
+  public ExpandedAssignmentProducerNode(String aKey, String aValue) {
+    key = aKey;
+    value = aValue;
+  }
+
+  public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure {
+    try {
+      ParameterExpander.setValueForKey(
+         aValueMap,
+         key,
+         ParameterExpander.expandExpression( aValueMap, value ));
+
+    }
+    catch (Throwable t) {
+      throw new ProducerFailure(t.getMessage(), t);
+    }
+  };
+
+  public Set buildVerbSet() {
+    return new HashSet();
+  }
+}
\ No newline at end of file
diff --git a/source/mir/producer/GeneratingProducerNode.java b/source/mir/producer/GeneratingProducerNode.java
new file mode 100755 (executable)
index 0000000..07bece9
--- /dev/null
@@ -0,0 +1,66 @@
+package mir.producer;
+
+import java.util.*;
+import java.io.*;
+import mir.util.*;
+import mir.producer.*;
+import mir.generator.*;
+
+public class GeneratingProducerNode implements ProducerNode {
+  private String generatorExpression;
+  private String destinationExpression;
+  private String parametersExpression;
+  private Generator.GeneratorLibrary generatorLibrary;
+  private WriterEngine writerEngine;
+
+  public GeneratingProducerNode(Generator.GeneratorLibrary aGeneratorLibrary, WriterEngine aWriterEngine, String aGenerator, String aDestination, String aParameters) {
+    generatorExpression=aGenerator;
+    destinationExpression=aDestination;
+    parametersExpression=aParameters;
+    generatorLibrary = aGeneratorLibrary;
+    writerEngine = aWriterEngine;
+  }
+
+  public GeneratingProducerNode(Generator.GeneratorLibrary aGeneratorLibrary, WriterEngine aWriterEngine, String aGenerator, String aDestination) {
+    this(aGeneratorLibrary, aWriterEngine, aGenerator, aDestination, "");
+  }
+
+  public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure {
+    Generator generator;
+    Object writer;
+    String generatorIdentifier;
+    String destinationIdentifier;
+    String parameters;
+
+    long startTime;
+    long endTime;
+
+    startTime = System.currentTimeMillis();
+    try {
+      destinationIdentifier = ParameterExpander.expandExpression( aValueMap, destinationExpression );
+      generatorIdentifier = ParameterExpander.expandExpression( aValueMap, generatorExpression );
+      parameters = ParameterExpander.expandExpression( aValueMap, parametersExpression );
+
+      aLogger.println("Generating " + generatorIdentifier + " into " + destinationIdentifier + " using parameters " + parameters);
+      aLogger.flush();
+
+      writer = writerEngine.openWriter( destinationIdentifier, parameters );
+      generator = generatorLibrary.makeGenerator( generatorIdentifier );
+      generator.generate(writer, aValueMap, aLogger);
+      writerEngine.closeWriter( writer );
+    }
+    catch (Throwable t) {
+      aLogger.println("  error while generating: " + t.getClass().getName() + ": " + t.getMessage());
+      t.printStackTrace(aLogger);
+      aLogger.flush();
+    }
+    endTime = System.currentTimeMillis();
+
+    aLogger.println("  Time: " + (endTime-startTime) + " ms<br>");
+    aLogger.flush();
+  }
+
+  public Set buildVerbSet() {
+    return new HashSet();
+  }
+}
\ No newline at end of file
diff --git a/source/mir/producer/LoggingProducerNode.java b/source/mir/producer/LoggingProducerNode.java
new file mode 100755 (executable)
index 0000000..e1d0451
--- /dev/null
@@ -0,0 +1,30 @@
+package mir.producer;
+
+import java.io.*;
+import java.util.*;
+import mir.util.*;
+import mir.producer.*;
+
+public class LoggingProducerNode implements ProducerNode {
+  private String expression;
+
+  public LoggingProducerNode(String anExpression) {
+    expression = anExpression;
+  }
+
+  public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure {
+    String text;
+
+    try {
+      text = ParameterExpander.expandExpression( aValueMap, expression );
+      aLogger.println(text);
+    }
+    catch (Throwable t) {
+      throw new ProducerFailure(t.getMessage(), t);
+    }
+  }
+
+  public Set buildVerbSet() {
+    return new HashSet();
+  }
+}
\ No newline at end of file
diff --git a/source/mir/producer/MediaProducerNode.java b/source/mir/producer/MediaProducerNode.java
deleted file mode 100755 (executable)
index e69de29..0000000
index 11b7b02..d3434d5 100755 (executable)
@@ -14,7 +14,7 @@ public class NodedProducer implements Producer {
     baseValues = aBaseValues;
   }
 
-  public void produce( PrintWriter aLogger ) throws ProducerFailure {
+  public void produce( PrintWriter aLogger ) throws ProducerFailure, ProducerExc {
     Map valueMap;
 
     valueMap = new HashMap();
diff --git a/source/mir/producer/NodedProducerFactory.java b/source/mir/producer/NodedProducerFactory.java
new file mode 100755 (executable)
index 0000000..b17be99
--- /dev/null
@@ -0,0 +1,39 @@
+package mir.producer;
+
+import java.util.*;
+import mir.producer.*;
+
+public class NodedProducerFactory implements ProducerFactory {
+  private ProducerNode rootNode;
+
+  public NodedProducerFactory(ProducerNode aRootNode) {
+    rootNode = aRootNode;
+  }
+
+  public mir.producer.Producer makeProducer(String aVerb, Map aBasicValueSet) throws ProducerFailure {
+    Map baseValues;
+
+    try {
+      baseValues = new HashMap();
+      baseValues.putAll(aBasicValueSet);
+
+      return new NodedProducer(rootNode, aVerb, baseValues);
+    }
+    catch (Throwable t) {
+      throw new ProducerFailure(t.getMessage(), t);
+    }
+  };
+
+  public Iterator verbs() {
+    Set verbSet = rootNode.buildVerbSet();
+
+    if (verbSet.isEmpty()) {
+      verbSet = new HashSet();
+
+      verbSet.add("(default)");
+    }
+
+    return verbSet.iterator();
+  };
+}
+
index ff57f36..47b894f 100755 (executable)
@@ -3,5 +3,5 @@ package mir.producer;
 import java.io.*;
 
 public interface Producer {
-  public void produce( PrintWriter aLogger ) throws ProducerFailure;
+  public void produce( PrintWriter aLogger ) throws ProducerFailure, ProducerExc;
 }
diff --git a/source/mir/producer/ProducerExc.java b/source/mir/producer/ProducerExc.java
new file mode 100755 (executable)
index 0000000..35065ec
--- /dev/null
@@ -0,0 +1,9 @@
+package mir.producer;
+
+import multex.Exc;
+
+public class ProducerExc extends Exc {
+  public ProducerExc(String aMessage) {
+    super(aMessage);
+  }
+}
index 6cb01be..7136d8c 100755 (executable)
@@ -3,7 +3,7 @@ package mir.producer;
 import java.util.*;
 
 public interface ProducerFactory {
-  public Producer makeProducer(String aVerb) throws ProducerFailure;
+  public Producer makeProducer(String aVerb, Map aStartingValues) throws ProducerFailure, ProducerExc;
   public Iterator verbs();
 }
 
index 580e1d0..0016c6a 100755 (executable)
@@ -7,4 +7,8 @@ public class ProducerFailure extends Failure {
   public ProducerFailure(String msg,Throwable cause) {
     super(msg,cause);
   }
+
+  public ProducerFailure(Throwable aCause) {
+    this (aCause.getMessage(), aCause);
+  }
 }
\ No newline at end of file
index 886a886..4f7b70a 100755 (executable)
@@ -4,6 +4,6 @@ import java.util.*;
 import java.io.*;
 
 public interface ProducerNode {
-  public void produce(Map aValueSet, String aVerb, PrintWriter aLogger);
+  public void produce(Map aValueSet, String aVerb, PrintWriter aLogger) throws ProducerExc, ProducerFailure;
   public Set buildVerbSet();
 }
\ No newline at end of file
index e93423d..9709499 100755 (executable)
@@ -10,11 +10,15 @@ public class ProducerNodeDecorator implements ProducerNode {
     slave = aSlave;
   }
 
-  public void produce(Map aValueSet, String aVerb, PrintWriter aLogger) throws ProducerFailure {
-    slave.produce(aValueSet, aVerb, aLogger);
+  public void produce(Map aValueSet, String aVerb, PrintWriter aLogger) throws ProducerFailure, ProducerExc {
+    if (slave!=null)
+      slave.produce(aValueSet, aVerb, aLogger);
   }
 
   public Set buildVerbSet() {
-    return slave.buildVerbSet();
+    if (slave!=null)
+      return slave.buildVerbSet();
+    else
+      return new HashSet();
   }
 }
\ No newline at end of file
index 1adbb49..37a9655 100755 (executable)
@@ -12,10 +12,7 @@ public class ResourceBundleProducerNode extends ProducerNodeDecorator {
   private String languageIdentifier;
 
   public ResourceBundleProducerNode(String aKey, String aBundleIdentifier, ProducerNode aSubNode) {
-    super(aSubNode);
-
-    bundleIdentifier = aBundleIdentifier;
-    key = aKey;
+    this (aKey, aBundleIdentifier, null, aSubNode);
   }
 
   public ResourceBundleProducerNode(String aKey, String aBundleIdentifier, String aLanguageIdentifier, ProducerNode aSubNode) {
@@ -42,9 +39,7 @@ public class ResourceBundleProducerNode extends ProducerNodeDecorator {
           MessageResources.getMessageResources(
               ParameterExpander.expandExpression( aValueMap, bundleIdentifier ));
       }
-      aValueMap.put(
-          key, messages
-      );
+      ParameterExpander.setValueForKey( aValueMap, key, messages );
 
       super.produce(aValueMap, aVerb, aLogger);
     }
diff --git a/source/mir/producer/reader/DefaultProducerNodeBuilders.java b/source/mir/producer/reader/DefaultProducerNodeBuilders.java
new file mode 100755 (executable)
index 0000000..3366afd
--- /dev/null
@@ -0,0 +1,597 @@
+package mir.producer.reader;
+
+import java.util.*;
+import mir.generator.*;
+import mir.producer.*;
+import mir.entity.adapter.*;
+import mir.util.*;
+
+public class DefaultProducerNodeBuilders {
+
+  public static void registerBuilders(ProducerNodeBuilderLibrary aBuilderLibrary,
+       EntityAdapterModel aModel, Generator.GeneratorLibrary aGeneratorLibrary,
+       WriterEngine aWriterEngine) {
+
+    aBuilderLibrary.registerBuilder("Set", EvaluatedAssignmentProducerNodeBuilder.class);
+    aBuilderLibrary.registerBuilder("Define", ExpandedAssignmentProducerNodeBuilder.class);
+    aBuilderLibrary.registerBuilder("Log", LoggingProducerNodeBuilder.class);
+    aBuilderLibrary.registerBuilder("Execute", ScriptCallingProducerNodeBuilder.class);
+    aBuilderLibrary.registerBuilder("Resource", ResourceBundleProducerNodeBuilder.class);
+
+    aBuilderLibrary.registerBuilder("DeleteFile", FileDeletingProducerNodeBuilder.class);
+    aBuilderLibrary.registerBuilder("SetFileDate", FileDateSettingProducerNodeBuilder.class);
+    aBuilderLibrary.registerBuilder("If", ConditionalProducerNodeBuilder.class);
+
+
+    aBuilderLibrary.registerFactory("Enumerate", new EnumeratingProducerNodeBuilder.factory(aModel));
+    aBuilderLibrary.registerFactory("List", new ListProducerNodeBuilder.factory(aModel));
+    aBuilderLibrary.registerFactory("Batch", new BatchingProducerNodeBuilder.factory(aModel));
+
+    aBuilderLibrary.registerFactory("Generate",
+        new GeneratingProducerNodeBuilder.factory(aGeneratorLibrary, aWriterEngine));
+  }
+
+  public static abstract class AbstractProducerNodeBuilder implements ProducerNodeBuilder {
+    private Map attributes;
+    private Map subNodes;
+    private Set availableSubnodes;
+
+    public AbstractProducerNodeBuilder(String anAvailableSubNodes[]) {
+      attributes = new HashMap();
+      subNodes = new HashMap();
+      availableSubnodes = new HashSet(Arrays.asList(anAvailableSubNodes));
+    }
+
+    protected ProducerNode getSubNode(String aName) {
+      return (ProducerNode) subNodes.get(aName);
+    }
+
+    public void setSubNode(String aName, ProducerNode aNode) {
+      subNodes.put(aName, aNode);
+    };
+
+    public Set getAvailableSubNodes() {
+      return availableSubnodes;
+    };
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  // general attribute names, specifc builders reference these, to keep attribute
+  //    names consistent
+
+  public final static String   SELECTION_ATTRIBUTE = "selection";
+  public final static String   ORDER_ATTRIBUTE = "order";
+  public final static String   DEFINITION_ATTRIBUTE = "table";
+  public final static String   SKIP_ATTRIBUTE = "skip";
+  public final static String   KEY_ATTRIBUTE = "key";
+  public final static String   LIMIT_ATTRIBUTE = "limit";
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class ExpandedAssignmentProducerNodeBuilder extends AbstractProducerNodeBuilder {
+    private final static String   ASSIGNMENT_KEY_ATTRIBUTE = KEY_ATTRIBUTE;
+    private final static String   ASSIGNMENT_VALUE_ATTRIBUTE = "value";
+    private final static String[] ASSIGNMENT_REQUIRED_ATTRIBUTES = { ASSIGNMENT_KEY_ATTRIBUTE, ASSIGNMENT_VALUE_ATTRIBUTE };
+    private final static String[] ASSIGNMENT_OPTIONAL_ATTRIBUTES = {};
+    private final static String[] ASSIGNMENT_SUBNODES = {};
+
+    private String key;
+    private String value;
+
+    public ExpandedAssignmentProducerNodeBuilder() {
+      super(ASSIGNMENT_SUBNODES);
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributes(anAttributes, ASSIGNMENT_REQUIRED_ATTRIBUTES, ASSIGNMENT_OPTIONAL_ATTRIBUTES);
+
+      key = (String) anAttributes.get(ASSIGNMENT_KEY_ATTRIBUTE);
+      value = (String) anAttributes.get(ASSIGNMENT_VALUE_ATTRIBUTE);
+    };
+
+    public ProducerNode constructNode() {
+      return new EvaluatedAssignmentProducerNode(key, value);
+    };
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class EvaluatedAssignmentProducerNodeBuilder extends AbstractProducerNodeBuilder {
+    private final static String   ASSIGNMENT_KEY_ATTRIBUTE = KEY_ATTRIBUTE;
+    private final static String   ASSIGNMENT_VALUE_ATTRIBUTE = "value";
+    private final static String[] ASSIGNMENT_REQUIRED_ATTRIBUTES = { ASSIGNMENT_KEY_ATTRIBUTE, ASSIGNMENT_VALUE_ATTRIBUTE };
+    private final static String[] ASSIGNMENT_OPTIONAL_ATTRIBUTES = {};
+    private final static String[] ASSIGNMENT_SUBNODES = {};
+
+    private String key;
+    private String value;
+
+    public EvaluatedAssignmentProducerNodeBuilder() {
+      super(ASSIGNMENT_SUBNODES);
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributes(anAttributes, ASSIGNMENT_REQUIRED_ATTRIBUTES, ASSIGNMENT_OPTIONAL_ATTRIBUTES);
+
+      key = (String) anAttributes.get(ASSIGNMENT_KEY_ATTRIBUTE);
+      value = (String) anAttributes.get(ASSIGNMENT_VALUE_ATTRIBUTE);
+    };
+
+    public ProducerNode constructNode() {
+      return new EvaluatedAssignmentProducerNode(key, value);
+    };
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class EnumeratingProducerNodeBuilder extends AbstractProducerNodeBuilder {
+    // ML: add limit, skip!
+    private final static String   ENUMERATION_KEY_ATTRIBUTE = KEY_ATTRIBUTE;
+    private final static String   ENUMERATION_DEFINITION_ATTRIBUTE = DEFINITION_ATTRIBUTE;
+    private final static String   ENUMERATION_SELECTION_ATTRIBUTE = SELECTION_ATTRIBUTE;
+    private final static String   ENUMERATION_ORDER_ATTRIBUTE = ORDER_ATTRIBUTE;
+    private final static String   ENUMERATION_DEFAULT_SUBNODE = "default";
+    private final static String[] ENUMERATION_REQUIRED_ATTRIBUTES = { ENUMERATION_KEY_ATTRIBUTE, ENUMERATION_DEFINITION_ATTRIBUTE };
+    private final static String[] ENUMERATION_OPTIONAL_ATTRIBUTES = { ENUMERATION_SELECTION_ATTRIBUTE, ENUMERATION_ORDER_ATTRIBUTE};
+    private final static String[] ENUMERATION_SUBNODES = {ENUMERATION_DEFAULT_SUBNODE};
+
+    private String key;
+    private String definition;
+    private String selection;
+    private String order;
+    private EntityAdapterModel model;
+
+    public EnumeratingProducerNodeBuilder(EntityAdapterModel aModel) {
+      super(ENUMERATION_SUBNODES);
+
+      model = aModel;
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc  {
+      ReaderTool.checkAttributes(anAttributes, ENUMERATION_REQUIRED_ATTRIBUTES, ENUMERATION_OPTIONAL_ATTRIBUTES);
+
+      key = (String) anAttributes.get(ENUMERATION_KEY_ATTRIBUTE);
+      definition = (String) anAttributes.get(ENUMERATION_DEFINITION_ATTRIBUTE);
+      selection = (String) anAttributes.get(ENUMERATION_SELECTION_ATTRIBUTE);
+      order = (String) anAttributes.get(ENUMERATION_ORDER_ATTRIBUTE);
+    };
+
+    public ProducerNode constructNode() {
+      return new EntityEnumeratingProducerNode(key, model, definition, selection, order, getSubNode(ENUMERATION_DEFAULT_SUBNODE ));
+    };
+
+    public static class factory implements ProducerNodeBuilderFactory {
+      private EntityAdapterModel model;
+
+      public factory(EntityAdapterModel aModel) {
+        model = aModel;
+      }
+
+      public ProducerNodeBuilder makeBuilder() {
+        return new EnumeratingProducerNodeBuilder(model);
+      }
+    }
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class ListProducerNodeBuilder extends AbstractProducerNodeBuilder {
+    private final static String   LIST_KEY_ATTRIBUTE = KEY_ATTRIBUTE;
+    private final static String   LIST_DEFINITION_ATTRIBUTE = DEFINITION_ATTRIBUTE;
+    private final static String   LIST_SELECTION_ATTRIBUTE = SELECTION_ATTRIBUTE;
+    private final static String   LIST_ORDER_ATTRIBUTE = ORDER_ATTRIBUTE;
+    private final static String   LIST_DEFAULT_SUBNODE = "default";
+    private final static String   LIST_LIMIT_ATTRIBUTE = LIMIT_ATTRIBUTE;
+    private final static String   LIST_SKIP_ATTRIBUTE = SKIP_ATTRIBUTE;
+    private final static String[] LIST_REQUIRED_ATTRIBUTES = { LIST_KEY_ATTRIBUTE, LIST_DEFINITION_ATTRIBUTE };
+    private final static String[] LIST_OPTIONAL_ATTRIBUTES = { LIST_SELECTION_ATTRIBUTE, LIST_ORDER_ATTRIBUTE, LIST_SKIP_ATTRIBUTE, LIST_LIMIT_ATTRIBUTE};
+    private final static String[] LIST_SUBNODES = {LIST_DEFAULT_SUBNODE};
+
+    private String key;
+    private String definition;
+    private String selection;
+    private String order;
+    private int limit;
+    private int skip;
+    private EntityAdapterModel model;
+
+    public ListProducerNodeBuilder(EntityAdapterModel aModel) {
+      super(LIST_SUBNODES);
+
+      model = aModel;
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      String limitString;
+      String skipString;
+
+      ReaderTool.checkAttributes(anAttributes, LIST_REQUIRED_ATTRIBUTES, LIST_OPTIONAL_ATTRIBUTES);
+
+      key = (String) anAttributes.get(LIST_KEY_ATTRIBUTE);
+      definition = (String) anAttributes.get(LIST_DEFINITION_ATTRIBUTE);
+      selection = (String) anAttributes.get(LIST_SELECTION_ATTRIBUTE);
+      order = (String) anAttributes.get(LIST_ORDER_ATTRIBUTE);
+      skip = 0;
+      limit = -1;
+
+      limitString = (String) anAttributes.get(LIST_LIMIT_ATTRIBUTE);
+      if (limitString!=null) {
+        try {
+          limit = Integer.parseInt(limitString);
+        }
+        catch (Throwable t) {
+          throw new ProducerConfigExc("invalid value for list parameter: " + limitString);
+        }
+      }
+      skipString = (String) anAttributes.get(LIST_SKIP_ATTRIBUTE);
+      if (skipString!=null) {
+        try {
+          skip = Integer.parseInt(skipString);
+        }
+        catch (Throwable t) {
+          throw new ProducerConfigExc("invalid value for skip parameter: " + skipString);
+        }
+      }
+    };
+
+    public ProducerNode constructNode() {
+      return new EntityListProducerNode(key, model, definition, selection, order, limit, skip, getSubNode(LIST_DEFAULT_SUBNODE ));
+    };
+
+    public static class factory implements ProducerNodeBuilderFactory {
+      private EntityAdapterModel model;
+
+      public factory(EntityAdapterModel aModel) {
+        model = aModel;
+      }
+
+      public ProducerNodeBuilder makeBuilder() {
+        return new ListProducerNodeBuilder(model);
+      }
+    }
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class LoggingProducerNodeBuilder extends AbstractProducerNodeBuilder {
+    private final static String   LOG_MESSAGE_ATTRIBUTE = "message";
+    private final static String[] LOG_REQUIRED_ATTRIBUTES = { LOG_MESSAGE_ATTRIBUTE };
+    private final static String[] LOG_OPTIONAL_ATTRIBUTES = {};
+    private final static String[] LOG_SUBNODES = {};
+
+    private String message;
+
+    public LoggingProducerNodeBuilder() {
+      super(LOG_SUBNODES);
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributes(anAttributes, LOG_REQUIRED_ATTRIBUTES, LOG_OPTIONAL_ATTRIBUTES);
+
+      message = (String) anAttributes.get(LOG_MESSAGE_ATTRIBUTE);
+    };
+
+    public ProducerNode constructNode() {
+      return new LoggingProducerNode(message);
+    };
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class ResourceBundleProducerNodeBuilder extends AbstractProducerNodeBuilder {
+    private final static String   RESOURCEBUNDLE_KEY_ATTRIBUTE = KEY_ATTRIBUTE;
+    private final static String   RESOURCEBUNDLE_BUNDLE_ATTRIBUTE = "bundle";
+    private final static String   RESOURCEBUNDLE_LANGUAGE_ATTRIBUTE = "language";
+    private final static String   RESOURCEBUNDLE_DEFAULT_SUBNODE = "default";
+    private final static String[] RESOURCEBUNDLE_REQUIRED_ATTRIBUTES = { RESOURCEBUNDLE_KEY_ATTRIBUTE, RESOURCEBUNDLE_BUNDLE_ATTRIBUTE };
+    private final static String[] RESOURCEBUNDLE_OPTIONAL_ATTRIBUTES = { RESOURCEBUNDLE_LANGUAGE_ATTRIBUTE};
+    private final static String[] RESOURCEBUNDLE_SUBNODES = {RESOURCEBUNDLE_DEFAULT_SUBNODE};
+
+    private String key;
+    private String bundle;
+    private String language;
+
+    public ResourceBundleProducerNodeBuilder() {
+      super(RESOURCEBUNDLE_SUBNODES);
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributes(anAttributes, RESOURCEBUNDLE_REQUIRED_ATTRIBUTES, RESOURCEBUNDLE_OPTIONAL_ATTRIBUTES);
+
+      key = (String) anAttributes.get(RESOURCEBUNDLE_KEY_ATTRIBUTE);
+      bundle = (String) anAttributes.get(RESOURCEBUNDLE_BUNDLE_ATTRIBUTE);
+      language = (String) anAttributes.get(RESOURCEBUNDLE_LANGUAGE_ATTRIBUTE);
+    };
+
+    public ProducerNode constructNode() {
+      return new ResourceBundleProducerNode(key, bundle, language, getSubNode(RESOURCEBUNDLE_DEFAULT_SUBNODE ));
+    };
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class FileDateSettingProducerNodeBuilder extends AbstractProducerNodeBuilder {
+    private final static String   FILEDATESETTING_FILE_ATTRIBUTE = "filename";
+    private final static String   FILEDATESETTING_DATE_ATTRIBUTE = "date";
+    private final static String[] FILEDATESETTING_REQUIRED_ATTRIBUTES = { FILEDATESETTING_FILE_ATTRIBUTE, FILEDATESETTING_DATE_ATTRIBUTE };
+    private final static String[] FILEDATESETTING_OPTIONAL_ATTRIBUTES = { };
+    private final static String[] FILEDATESETTING_SUBNODES = {};
+
+    private String fileNameKey;
+    private String dateKey;
+
+    public FileDateSettingProducerNodeBuilder() {
+      super(FILEDATESETTING_SUBNODES);
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributes(anAttributes, FILEDATESETTING_REQUIRED_ATTRIBUTES, FILEDATESETTING_OPTIONAL_ATTRIBUTES);
+
+      fileNameKey = (String) anAttributes.get(FILEDATESETTING_FILE_ATTRIBUTE);
+      dateKey = (String) anAttributes.get(FILEDATESETTING_DATE_ATTRIBUTE);
+    };
+
+    public ProducerNode constructNode() {
+      return new FileDateSettingProducerNode(fileNameKey, dateKey);
+    };
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class FileDeletingProducerNodeBuilder extends AbstractProducerNodeBuilder {
+    private final static String   FILEDELETING_FILE_ATTRIBUTE = "filename";
+    private final static String[] FILEDELETING_REQUIRED_ATTRIBUTES = { FILEDELETING_FILE_ATTRIBUTE };
+    private final static String[] FILEDELETING_OPTIONAL_ATTRIBUTES = { };
+    private final static String[] FILEDELETING_SUBNODES = { };
+
+    private String fileNameKey;
+
+    public FileDeletingProducerNodeBuilder() {
+      super(FILEDELETING_SUBNODES);
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributes(anAttributes, FILEDELETING_REQUIRED_ATTRIBUTES, FILEDELETING_OPTIONAL_ATTRIBUTES);
+
+      fileNameKey = (String) anAttributes.get(FILEDELETING_FILE_ATTRIBUTE);
+    };
+
+    public ProducerNode constructNode() {
+      return new FileDeletingProducerNode(fileNameKey);
+    };
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class ScriptCallingProducerNodeBuilder extends AbstractProducerNodeBuilder {
+    private final static String   SCRIPT_COMMAND_ATTRIBUTE = "command";
+    private final static String[] SCRIPT_REQUIRED_ATTRIBUTES = { SCRIPT_COMMAND_ATTRIBUTE };
+    private final static String[] SCRIPT_OPTIONAL_ATTRIBUTES = {};
+    private final static String[] SCRIPT_SUBNODES = {};
+
+    private String command;
+
+    public ScriptCallingProducerNodeBuilder() {
+      super(SCRIPT_SUBNODES);
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributes(anAttributes, SCRIPT_REQUIRED_ATTRIBUTES, SCRIPT_OPTIONAL_ATTRIBUTES);
+
+      command = (String) anAttributes.get(SCRIPT_COMMAND_ATTRIBUTE);
+    };
+
+    public ProducerNode constructNode() {
+      return new ScriptCallingProducerNode(command);
+    };
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class GeneratingProducerNodeBuilder extends AbstractProducerNodeBuilder {
+    private final static String   GENERATION_GENERATOR_ATTRIBUTE = "generator";
+    private final static String   GENERATION_DESTINATION_ATTRIBUTE = "destination";
+    private final static String   GENERATION_PARAMETERS_ATTRIBUTE = "parameters";
+    private final static String[] GENERATION_REQUIRED_ATTRIBUTES = { GENERATION_GENERATOR_ATTRIBUTE, GENERATION_DESTINATION_ATTRIBUTE };
+    private final static String[] GENERATION_OPTIONAL_ATTRIBUTES = { GENERATION_PARAMETERS_ATTRIBUTE};
+    private final static String[] GENERATION_SUBNODES = {};
+
+    private String generator;
+    private String destination;
+    private String parameters;
+    private Generator.GeneratorLibrary generatorLibrary;
+    private WriterEngine writerEngine;
+
+    public GeneratingProducerNodeBuilder(Generator.GeneratorLibrary aGeneratorLibrary, WriterEngine aWriterEngine) {
+      super(GENERATION_SUBNODES);
+
+      writerEngine = aWriterEngine;
+      generatorLibrary = aGeneratorLibrary;
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributes(anAttributes, GENERATION_REQUIRED_ATTRIBUTES, GENERATION_OPTIONAL_ATTRIBUTES);
+
+      generator = (String) anAttributes.get(GENERATION_GENERATOR_ATTRIBUTE);
+      destination = (String) anAttributes.get(GENERATION_DESTINATION_ATTRIBUTE);
+      parameters = ReaderTool.getStringAttributeWithDefault(anAttributes, GENERATION_PARAMETERS_ATTRIBUTE, "" );
+    };
+
+    public ProducerNode constructNode() {
+      return new GeneratingProducerNode(generatorLibrary, writerEngine, generator, destination, parameters);
+    };
+
+    public static class factory implements ProducerNodeBuilderFactory {
+      private Generator.GeneratorLibrary generatorLibrary;
+      private WriterEngine writerEngine;
+
+      public factory(Generator.GeneratorLibrary aGeneratorLibrary, WriterEngine aWriterEngine) {
+        writerEngine = aWriterEngine;
+        generatorLibrary = aGeneratorLibrary;
+      }
+
+      public ProducerNodeBuilder makeBuilder() {
+        return new GeneratingProducerNodeBuilder(generatorLibrary, writerEngine);
+      }
+    }
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class BatchingProducerNodeBuilder extends AbstractProducerNodeBuilder {
+
+    private final static String   BATCHER_DATAKEY_ATTRIBUTE = KEY_ATTRIBUTE;
+    private final static String   BATCHER_INFOKEY_ATTRIBUTE = "infokey";
+    private final static String   BATCHER_DEFINITION_ATTRIBUTE = DEFINITION_ATTRIBUTE;
+    private final static String   BATCHER_SELECTION_ATTRIBUTE = SELECTION_ATTRIBUTE;
+    private final static String   BATCHER_ORDER_ATTRIBUTE = ORDER_ATTRIBUTE;
+
+    private final static String   BATCHER_BATCHSIZE_ATTRIBUTE = "batchsize";
+    private final static String   BATCHER_MINBATCHSIZE_ATTRIBUTE = "minbatchsize";
+    private final static String   BATCHER_SKIP_ATTRIBUTE = SKIP_ATTRIBUTE;
+
+    private final static String   BATCHER_BATCH_SUBNODE = "batches";
+    private final static String   BATCHER_BATCHLIST_SUBNODE = "batchlist";
+    private final static String[] BATCHER_REQUIRED_ATTRIBUTES = { BATCHER_DATAKEY_ATTRIBUTE, BATCHER_INFOKEY_ATTRIBUTE, BATCHER_DEFINITION_ATTRIBUTE, BATCHER_BATCHSIZE_ATTRIBUTE };
+    private final static String[] BATCHER_OPTIONAL_ATTRIBUTES = { BATCHER_SELECTION_ATTRIBUTE, BATCHER_ORDER_ATTRIBUTE, BATCHER_MINBATCHSIZE_ATTRIBUTE, BATCHER_SKIP_ATTRIBUTE };
+    private final static String[] BATCHER_SUBNODES = { BATCHER_BATCH_SUBNODE, BATCHER_BATCHLIST_SUBNODE };
+
+    private EntityAdapterModel model;
+    private String batchDataKey;
+    private String batchInfoKey;
+    private String definition;
+    private String selection;
+    private String order;
+    private int batchSize;
+    private int minBatchSize;
+    private int skip;
+
+
+    public BatchingProducerNodeBuilder(EntityAdapterModel aModel) {
+      super(BATCHER_SUBNODES);
+
+      model = aModel;
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributes(anAttributes, BATCHER_REQUIRED_ATTRIBUTES, BATCHER_OPTIONAL_ATTRIBUTES);
+
+      batchDataKey = ReaderTool.getStringAttributeWithDefault(anAttributes, BATCHER_DATAKEY_ATTRIBUTE, "data" );
+      batchInfoKey = ReaderTool.getStringAttributeWithDefault(anAttributes, BATCHER_INFOKEY_ATTRIBUTE, "info" );
+      definition = ReaderTool.getStringAttributeWithDefault(anAttributes, BATCHER_DEFINITION_ATTRIBUTE, "" );
+      selection = ReaderTool.getStringAttributeWithDefault(anAttributes, BATCHER_SELECTION_ATTRIBUTE, "" );
+      order = ReaderTool.getStringAttributeWithDefault(anAttributes, BATCHER_ORDER_ATTRIBUTE, "" );
+
+      batchSize = ReaderTool.getIntegerAttributeWithDefault(anAttributes, BATCHER_BATCHSIZE_ATTRIBUTE, 20 );
+      minBatchSize = ReaderTool.getIntegerAttributeWithDefault(anAttributes, BATCHER_MINBATCHSIZE_ATTRIBUTE, 0 );
+      skip = ReaderTool.getIntegerAttributeWithDefault(anAttributes, BATCHER_SKIP_ATTRIBUTE, 0 );
+    };
+
+    public ProducerNode constructNode() {
+      return new EntityBatchingProducerNode(
+          batchDataKey,
+          batchInfoKey,
+          model,
+          definition,
+          selection,
+          order,
+          batchSize,
+          minBatchSize,
+          skip,
+          getSubNode( BATCHER_BATCH_SUBNODE ),
+          getSubNode( BATCHER_BATCHLIST_SUBNODE )
+      );
+    };
+
+    public static class factory implements ProducerNodeBuilderFactory {
+      private EntityAdapterModel model;
+
+      public factory(EntityAdapterModel aModel) {
+        model = aModel;
+      }
+
+      public ProducerNodeBuilder makeBuilder() {
+        return new BatchingProducerNodeBuilder(model);
+      }
+    }
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class ConditionalProducerNodeBuilder extends AbstractProducerNodeBuilder {
+    private final static String   IF_CONDITION_ATTRIBUTE = "condition";
+
+    private final static String   IF_TRUE_SUBNODE = "then";
+    private final static String   IF_FALSE_SUBNODE = "else";
+    private final static String[] IF_REQUIRED_ATTRIBUTES = { IF_CONDITION_ATTRIBUTE };
+    private final static String[] IF_OPTIONAL_ATTRIBUTES = {  };
+    private final static String[] IF_SUBNODES = { IF_TRUE_SUBNODE, IF_FALSE_SUBNODE };
+
+    private String condition;
+
+    public ConditionalProducerNodeBuilder() {
+      super(IF_SUBNODES);
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributes(anAttributes, IF_REQUIRED_ATTRIBUTES, IF_OPTIONAL_ATTRIBUTES);
+
+      condition = (String) anAttributes.get( IF_CONDITION_ATTRIBUTE );
+    };
+
+    public ProducerNode constructNode() {
+      return new ConditionalProducerNode(
+          condition,
+          getSubNode( IF_TRUE_SUBNODE ),
+          getSubNode( IF_FALSE_SUBNODE )
+      );
+    };
+
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
+  public static class ScriptedProducerNodeBuilder implements ProducerNodeBuilder {
+    private ScriptedProducerNodeDefinition definition;
+    private Map nodeParameterValues;
+    private Map parameterValues;
+
+    public ScriptedProducerNodeBuilder(ScriptedProducerNodeDefinition aDefinition) {
+      definition = aDefinition;
+
+      parameterValues = new HashMap();
+      parameterValues.putAll(definition.getParameters());
+
+      nodeParameterValues = new HashMap();
+    }
+
+    public void setSubNode(String aName, ProducerNode aNode) {
+      nodeParameterValues.put(aName, aNode);
+    };
+
+    public Set getAvailableSubNodes() {
+      return definition.getNodeParameters();
+    };
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributeSet(anAttributes.keySet(), definition.getRequiredAttributes(), definition.getOptionalAttributes());
+    };
+
+    public ProducerNode constructNode() {
+      return new ScriptedProducerNode(definition, parameterValues, nodeParameterValues);
+    };
+
+    public static class factory implements ProducerNodeBuilderFactory {
+      private ScriptedProducerNodeDefinition definition;
+
+      public factory(ScriptedProducerNodeDefinition aDefinition) {
+        definition = aDefinition;
+      }
+
+      public ProducerNodeBuilder makeBuilder() {
+        return new ScriptedProducerNodeBuilder(definition);
+      }
+    }
+  }
+}
diff --git a/source/mir/producer/reader/ProducerConfigExc.java b/source/mir/producer/reader/ProducerConfigExc.java
new file mode 100755 (executable)
index 0000000..15cb7c9
--- /dev/null
@@ -0,0 +1,9 @@
+package mir.producer.reader;
+
+import multex.Exc;
+
+public class ProducerConfigExc extends Exc {
+  public ProducerConfigExc(String aMessage) {
+    super(aMessage);
+  }
+}
diff --git a/source/mir/producer/reader/ProducerConfigFailure.java b/source/mir/producer/reader/ProducerConfigFailure.java
new file mode 100755 (executable)
index 0000000..23ae14d
--- /dev/null
@@ -0,0 +1,14 @@
+
+package mir.producer.reader;
+
+import multex.Failure;
+
+public class ProducerConfigFailure extends Failure {
+  public ProducerConfigFailure(String aMessage, Throwable aCause) {
+    super(aMessage, aCause);
+  }
+
+  public ProducerConfigFailure(Throwable aCause) {
+    super(aCause.getMessage(), aCause);
+  }
+}
diff --git a/source/mir/producer/reader/ProducerConfigReader.java b/source/mir/producer/reader/ProducerConfigReader.java
new file mode 100755 (executable)
index 0000000..f673250
--- /dev/null
@@ -0,0 +1,699 @@
+package  mir.producer.reader;
+
+import java.io.*;
+import java.util.*;
+import java.lang.System;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.*;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import mir.util.*;
+import mir.config.exceptions.*;
+import mir.producer.*;
+
+//import mir.producer.exceptions.*;
+import mir.misc.Location;
+
+public class ProducerConfigReader {
+  private ProducerNodeBuilderLibrary builderLibrary;
+  private ProducerNodeBuilderLibrary scriptedNodeBuilderLibrary;
+
+  public ProducerConfigReader() {
+    super();
+  };
+
+  public void parseFile(String aFileName, ProducerNodeBuilderLibrary aBuilderLibrary, Map aProducerFactories) throws ConfigFailure {
+    parseFile(aFileName, aBuilderLibrary, aProducerFactories, new Vector());
+
+  }
+
+  public void parseFile(String aFileName, ProducerNodeBuilderLibrary aBuilderLibrary, Map aProducerFactories, List aUsedFiles) throws ConfigFailure {
+    try {
+      builderLibrary = aBuilderLibrary;
+      scriptedNodeBuilderLibrary = new ProducerNodeBuilderLibrary();
+
+      SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+
+      parserFactory.setNamespaceAware(false);
+      parserFactory.setValidating(true);
+
+      ProducerConfigHandler handler = new ProducerConfigHandler(parserFactory, aProducerFactories, aUsedFiles);
+
+      handler.includeFile(aFileName);
+    }
+    catch (Throwable e) {
+      if (e instanceof SAXParseException && ((SAXParseException) e).getException() instanceof ConfigFailure) {
+        throw (ConfigFailure) ((SAXParseException) e).getException();
+      }
+      else {
+        e.printStackTrace();
+        throw new ConfigFailure( e.getMessage() );
+      }
+    }
+  }
+
+  private class ProducerConfigHandler extends DefaultHandler {
+    private Locator locator;
+    private Stack includeFileStack;
+    private SAXParserFactory parserFactory;
+    private SectionsManager manager;
+    private List usedFiles;
+    private InputSource inputSource;
+
+    public ProducerConfigHandler(SAXParserFactory aParserFactory, Map aProducers, List aUsedFiles) {
+      super();
+
+      includeFileStack=new Stack();
+      parserFactory=aParserFactory;
+      includeFileStack = new Stack();
+      manager = new SectionsManager();
+      usedFiles = aUsedFiles;
+
+      manager.pushHandler(new RootSectionHandler(aProducers));
+   }
+
+    public String getLocatorDescription(Locator aLocator) {
+      return aLocator.getPublicId()+" ("+aLocator.getLineNumber()+")";
+    }
+
+    public void setDocumentLocator(Locator aLocator) {
+      locator=aLocator;
+    }
+
+    private void includeFile(String aFileName) throws ConfigFailure, SAXParseException, SAXException {
+      File file;
+      SAXParser parser;
+
+      try {
+        if (!includeFileStack.empty())
+          file = new File(new File((String) includeFileStack.peek()).getParent(), aFileName);
+        else
+          file = new File(aFileName);
+
+        System.err.println("about to include "+file.getCanonicalPath());
+
+        if (includeFileStack.contains(file.getCanonicalPath())) {
+          throw new ConfigFailure("recursive inclusion of file "+file.getCanonicalPath(), getLocatorDescription(locator));
+        }
+
+        usedFiles.add(file);
+
+        parser=parserFactory.newSAXParser();
+
+        inputSource = new InputSource(new FileInputStream(file));
+        inputSource.setPublicId(file.getCanonicalPath());
+
+        includeFileStack.push(file.getCanonicalPath());
+        try {
+          parser.parse(inputSource, this);
+        }
+        finally {
+          includeFileStack.pop();
+        }
+      }
+      catch (ParserConfigurationException e) {
+        throw new ConfigFailure("Internal exception while including \""+aFileName+"\": "+e.getMessage(), e, getLocatorDescription(locator));
+      }
+      catch (SAXParseException e) {
+        throw e;
+      }
+      catch (ConfigFailure e) {
+        throw e;
+      }
+      catch (FileNotFoundException e) {
+        throw new ConfigFailure("Include file \""+aFileName+"\" not found: "+e.getMessage(), e, getLocatorDescription(locator));
+      }
+      catch (IOException e) {
+        throw new ConfigFailure("unable to open include file \""+aFileName+"\": "+e.getMessage(), e, getLocatorDescription(locator));
+      }
+    }
+
+    public void startElement(String aUri, String aTag, String aQualifiedName, Attributes anAttributes) throws SAXException {
+      Map attributesMap;
+      int i;
+
+      try {
+        if (aQualifiedName.equals("include")) {
+          String fileName=anAttributes.getValue("file");
+
+          if (fileName==null) {
+            throw new ConfigFailure("include has no file attribute", getLocatorDescription(locator));
+          }
+
+          includeFile(fileName);
+        }
+        else {
+          attributesMap = new HashMap();
+          for (i=0; i<anAttributes.getLength(); i++)
+            attributesMap.put(anAttributes.getQName(i), anAttributes.getValue(i));
+
+          manager.pushHandler( manager.currentHandler().startElement(aQualifiedName, attributesMap) );
+        }
+      }
+      catch (Exception e) {
+        e.printStackTrace(System.out);
+        throw new SAXException(e);
+      }
+    }
+
+    public void endElement(String aUri, String aTag, String aQualifiedName) throws SAXException {
+      try
+      {
+        if (!aQualifiedName.equals("include")) {
+          SectionHandler handler = manager.popHandler();
+
+          handler.finishSection();
+
+          if (!manager.isEmpty()) {
+            manager.currentHandler().endElement(handler);
+          }
+        }
+      }
+      catch (Exception e) {
+        e.printStackTrace(System.out);
+        throw new SAXException(e);
+      }
+    }
+
+    public void characters(char[] aBuffer, int aStart, int anEnd) throws SAXParseException {
+      String text = new String(aBuffer, aStart, anEnd).trim();
+      if ( text.length() > 0) {
+        throw new SAXParseException("Text not allowed", locator, new ConfigFailure("text not allowed", getLocatorDescription(locator)));
+      }
+    }
+
+  }
+  public class SectionsManager {
+    Stack handlerStack;
+
+    public SectionsManager() {
+      handlerStack = new Stack();
+    }
+
+    public void pushHandler(SectionHandler aSectionHandler) {
+      handlerStack.push(aSectionHandler);
+    }
+
+    public SectionHandler popHandler() {
+      return (SectionHandler) handlerStack.pop();
+    }
+
+    public SectionHandler currentHandler() {
+      return (SectionHandler) handlerStack.peek();
+    }
+
+    public boolean isEmpty() {
+      return handlerStack.isEmpty();
+    }
+  }
+
+  public abstract class SectionHandler {
+    public abstract SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc;
+
+    public abstract void endElement(SectionHandler aHandler) throws ProducerConfigExc;
+//    {
+//    }
+
+    public void finishSection() throws ProducerConfigExc {
+    }
+  }
+
+  public class RootSectionHandler extends SectionHandler {
+    private Map producers;
+
+    public RootSectionHandler(Map aProducers) {
+      producers = aProducers;
+    }
+
+    public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc {
+      if (aTag.equals("producers")) {
+        return new ProducersSectionHandler(producers);
+      }
+      else
+        throw new ProducerConfigExc ("Tag 'producers' expected, tag '"+aTag+"' found");
+    }
+
+    public void endElement(SectionHandler aHandler) {
+    }
+
+    public void finishSection() throws ProducerConfigExc {
+    }
+  }
+
+
+  private final static String   PRODUCER_NAME_ATTRIBUTE = "name";
+  private final static String[] PRODUCER_REQUIRED_ATTRIBUTES = { PRODUCER_NAME_ATTRIBUTE };
+  private final static String[] PRODUCER_OPTIONAL_ATTRIBUTES = { };
+
+  private final static String   NODE_DEFINITION_NAME_ATTRIBUTE = "name";
+  private final static String[] NODE_DEFINITION_REQUIRED_ATTRIBUTES = { NODE_DEFINITION_NAME_ATTRIBUTE };
+  private final static String[] NODE_DEFINITION_OPTIONAL_ATTRIBUTES = {  };
+
+  public class ProducersSectionHandler extends SectionHandler {
+    private Map producers;
+    private String name;
+
+    public ProducersSectionHandler(Map aProducers) {
+      producers = aProducers;
+    }
+
+    public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc {
+
+      if (aTag.equals("producer")) {
+        ReaderTool.checkAttributes(anAttributes, PRODUCER_REQUIRED_ATTRIBUTES, PRODUCER_OPTIONAL_ATTRIBUTES);
+
+        name = (String) anAttributes.get(PRODUCER_NAME_ATTRIBUTE);
+        ReaderTool.checkValidIdentifier( name );
+
+        if (producers.containsKey(name))
+          throw new ProducerConfigExc("Duplicate producer name: '" + name + "'");
+
+        name = (String) anAttributes.get(PRODUCER_NAME_ATTRIBUTE);
+
+        return new ProducerSectionHandler();
+      }
+      else if (aTag.equals("nodedefinition")) {
+        ReaderTool.checkAttributes(anAttributes, NODE_DEFINITION_REQUIRED_ATTRIBUTES, NODE_DEFINITION_OPTIONAL_ATTRIBUTES);
+
+        name = (String) anAttributes.get(NODE_DEFINITION_NAME_ATTRIBUTE);
+        ReaderTool.checkValidIdentifier( name );
+
+        if (producers.containsKey(name))
+          throw new ProducerConfigExc("Duplicate producer name: '" + name + "'");
+
+        name = (String) anAttributes.get(NODE_DEFINITION_NAME_ATTRIBUTE);
+
+        return new NodeDefinitionSectionHandler(name);
+      }
+
+      throw new ProducerConfigExc("Unexpected tag: "+aTag );
+    }
+
+    public void endElement(SectionHandler aHandler) throws ProducerConfigExc {
+      if (aHandler instanceof ProducerSectionHandler) {
+        producers.put(name, ((ProducerSectionHandler) aHandler).getProducerFactory());
+      }
+      else if (aHandler instanceof NodeDefinitionSectionHandler) {
+
+        scriptedNodeBuilderLibrary.registerFactory(name,
+            new DefaultProducerNodeBuilders.ScriptedProducerNodeBuilder.factory(
+                ((NodeDefinitionSectionHandler) aHandler).getDefinition()));
+      }
+      else throw new ProducerConfigExc("ProducersSectionHandler.endElement Internal error: Unexpected handler: " + aHandler.getClass().getName());
+    }
+
+    public void finishSection() throws ProducerConfigExc {
+    }
+  }
+
+  public class ProducerSectionHandler extends SectionHandler {
+    private ProducerFactory producerFactory;
+
+    private ProducerNode body;
+    private Map verbs;
+    private String defaultVerb;
+
+    public SectionHandler startElement(String aTag, Map anAttributes)  throws ProducerConfigExc {
+      if (aTag.equals("verbs")) {
+        if (verbs!=null)
+          throw new ProducerConfigExc("Verbs already processed");
+        if (body!=null)
+          throw new ProducerConfigExc("Verbs should come before body");
+        else
+          return new ProducerVerbsSectionHandler();
+      }
+      else if (aTag.equals("body")) {
+        if (body==null)
+          return new ProducerNodeSectionHandler();
+        else
+          throw new ProducerConfigExc("Body already processed");
+      }
+      throw new ProducerConfigExc("Unexpected tag: '"+aTag+"'");
+    }
+
+    public void endElement(SectionHandler aHandler) throws ProducerConfigExc {
+      if (aHandler instanceof ProducerNodeSectionHandler) {
+        body = ((ProducerNodeSectionHandler) aHandler).getProducerNode();
+      }
+      else if (aHandler instanceof ProducerVerbsSectionHandler)
+      {
+        verbs = ((ProducerVerbsSectionHandler) aHandler).getVerbs();
+        defaultVerb = ((ProducerVerbsSectionHandler) aHandler).getDefaultVerb();
+      }
+      else throw new ProducerConfigExc("ProducerSectionHandler.endElement Internal error: Unexpected handler: " + aHandler.getClass().getName());
+    }
+
+    public void finishSection() throws ProducerConfigExc {
+      if (verbs==null)
+        throw new ProducerConfigExc("No verbs defined");
+
+      if (body==null)
+        throw new ProducerConfigExc("No body defined");
+
+      producerFactory = new ScriptedProducerFactory(verbs, body, defaultVerb);
+    }
+
+    public ProducerFactory getProducerFactory() {
+      return producerFactory;
+    }
+  }
+
+  private final static String   PRODUCER_VERB_NAME_ATTRIBUTE = "name";
+  private final static String   PRODUCER_VERB_DEFAULT_ATTRIBUTE = "default";
+  private final static String[] PRODUCER_VERB_REQUIRED_ATTRIBUTES = { PRODUCER_VERB_NAME_ATTRIBUTE };
+  private final static String[] PRODUCER_VERB_OPTIONAL_ATTRIBUTES = { PRODUCER_VERB_DEFAULT_ATTRIBUTE };
+
+  public class ProducerVerbsSectionHandler extends SectionHandler {
+    private Map verbs;
+    private String defaultVerb;
+    private String currentVerb;
+
+    public ProducerVerbsSectionHandler() {
+      verbs = new HashMap();
+      defaultVerb = null;
+    }
+
+    public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc {
+      if (aTag.equals("verb")) {
+        ReaderTool.checkAttributes(anAttributes, PRODUCER_VERB_REQUIRED_ATTRIBUTES, PRODUCER_VERB_OPTIONAL_ATTRIBUTES);
+        currentVerb = (String) anAttributes.get( PRODUCER_VERB_NAME_ATTRIBUTE );
+
+        ReaderTool.checkValidIdentifier( currentVerb );
+
+        if (verbs.containsKey(currentVerb))
+          throw new ProducerConfigExc( "Duplicate definition of verb '" + currentVerb + "'" );
+
+        if (anAttributes.containsKey(PRODUCER_VERB_DEFAULT_ATTRIBUTE)) {
+          if (defaultVerb!=null)
+            throw new ProducerConfigExc( "Default verb already declared" );
+
+          defaultVerb = currentVerb;
+        }
+
+        return new ProducerNodeSectionHandler();
+      }
+      else throw new ProducerConfigExc("Only 'verb' tags allowed here, '" + aTag + "' encountered.");
+    }
+
+    public void endElement(SectionHandler aHandler) {
+      verbs.put(currentVerb, ((ProducerNodeSectionHandler) aHandler).getProducerNode());
+    }
+
+    public void finishSection() {
+    }
+
+    public String getDefaultVerb() {
+      return defaultVerb;
+    }
+
+    public Map getVerbs() {
+      return verbs;
+    }
+  }
+
+  public class EmptySectionHandler extends SectionHandler {
+    public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc {
+      throw new ProducerConfigExc("No tags are allowed here");
+    }
+
+    public void endElement(SectionHandler aHandler) {
+    }
+
+  }
+
+  public class MultiProducerNodeSectionHandler extends SectionHandler {
+    private Map nodeParameters;
+    private Set validNodeParameters;
+    private String currentNodeParameter;
+    private String scriptedNodeName;
+    private Set allowedNodeParameterReferences;
+
+    public MultiProducerNodeSectionHandler(String aScriptedNodeName, Set anAllowedNodeParameterReferences, Set aValidNodeParameters) {
+      allowedNodeParameterReferences = anAllowedNodeParameterReferences;
+      scriptedNodeName = aScriptedNodeName;
+      validNodeParameters = aValidNodeParameters;
+      nodeParameters = new HashMap();
+    }
+    public MultiProducerNodeSectionHandler(Set aValidNodeParameters) {
+      this("", new HashSet(), aValidNodeParameters);
+    }
+
+    public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc {
+      if (!validNodeParameters.contains(aTag))
+        throw new ProducerConfigExc("Invalid node parameter: '" + aTag + "'");
+      else if (nodeParameters.containsKey(aTag))
+        throw new ProducerConfigExc("Node parameter: '" + aTag + "' already specified");
+      else if (anAttributes.size()>0)
+        throw new ProducerConfigExc("No parameters are allowed here");
+
+      currentNodeParameter = aTag;
+
+      return new ProducerNodeSectionHandler(scriptedNodeName, validNodeParameters);
+    }
+
+    public void endElement(SectionHandler aHandler) throws ProducerConfigExc  {
+      if (aHandler instanceof ProducerNodeSectionHandler) {
+        nodeParameters.put(currentNodeParameter, ((ProducerNodeSectionHandler) aHandler).getProducerNode());
+      }
+      else {
+        throw new ProducerConfigExc("Internal error: unknown section handler '" + aHandler.getClass().getName() + "'" );
+      }
+    }
+
+    public Map getNodeParameters() {
+      return nodeParameters;
+    }
+  }
+
+  public class ProducerNodeSectionHandler extends SectionHandler {
+    private CompositeProducerNode producerNode;
+    private ProducerNodeBuilder currentBuilder;
+    private String scriptedNodeName;
+    private Set allowedNodeParameterReferences;
+
+    public ProducerNodeSectionHandler(String aScriptedNodeName, Set anAllowedNodeParameterReferences) {
+      producerNode = new CompositeProducerNode();
+      scriptedNodeName = aScriptedNodeName;
+      allowedNodeParameterReferences = anAllowedNodeParameterReferences;
+    }
+
+    public ProducerNodeSectionHandler() {
+      this("", new HashSet());
+    }
+
+    public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc {
+      if (allowedNodeParameterReferences.contains((aTag))) {
+        if (!anAttributes.isEmpty()) {
+          throw new ProducerConfigExc( "No attributes allowed" );
+        }
+        producerNode.addSubNode(new ScriptedProducerNodeDefinition.NodeParameterProducerNode(scriptedNodeName, aTag));
+
+        return new EmptySectionHandler();
+      }
+      else if (scriptedNodeBuilderLibrary.hasBuilderForName(aTag) || builderLibrary.hasBuilderForName((aTag))) {
+
+        if (scriptedNodeBuilderLibrary.hasBuilderForName(aTag))
+          currentBuilder = scriptedNodeBuilderLibrary.constructBuilder(aTag);
+        else
+          currentBuilder = builderLibrary.constructBuilder(aTag);
+
+        currentBuilder.setAttributes(anAttributes);
+        if (currentBuilder.getAvailableSubNodes().isEmpty())  {
+          return new EmptySectionHandler();
+        }
+        if (currentBuilder.getAvailableSubNodes().size()>1)
+          return new MultiProducerNodeSectionHandler(scriptedNodeName, allowedNodeParameterReferences, currentBuilder.getAvailableSubNodes());
+        else if (currentBuilder.getAvailableSubNodes().size()<1)
+          return new EmptySectionHandler();
+        else {
+          return new ProducerNodeSectionHandler(scriptedNodeName, allowedNodeParameterReferences);
+        }
+      }
+      else
+        throw new ProducerConfigExc("Unknown producer node tag: '" + aTag + "'");
+    }
+
+    public void endElement(SectionHandler aHandler) throws ProducerConfigExc  {
+      if (aHandler instanceof ProducerNodeSectionHandler) {
+        currentBuilder.setSubNode((String) (currentBuilder.getAvailableSubNodes().iterator().next()),
+                    ((ProducerNodeSectionHandler) aHandler).getProducerNode());
+      }
+      else if (aHandler instanceof MultiProducerNodeSectionHandler) {
+        Iterator i;
+        Map nodeParameters;
+        Map.Entry entry;
+
+        nodeParameters = ((MultiProducerNodeSectionHandler) aHandler).getNodeParameters();
+        i = nodeParameters.entrySet().iterator();
+        while (i.hasNext()) {
+          entry = (Map.Entry) i.next();
+          currentBuilder.setSubNode((String) entry.getKey(), (ProducerNode) entry.getValue());
+        }
+      }
+      else if (aHandler instanceof EmptySectionHandler) {
+        // deliberately empty: nothing expected, so nothing to process
+      }
+      else {
+        throw new ProducerConfigExc("Internal error: unknown section handler '" + aHandler.getClass().getName() + "'" );
+      }
+
+      producerNode.addSubNode(currentBuilder.constructNode());
+      currentBuilder = null;
+    }
+
+    public ProducerNode getProducerNode() {
+      if (producerNode.getNrSubNodes()==1) {
+        return producerNode.getSubNode(0);
+      }
+      else {
+        return producerNode;
+      }
+    }
+  }
+
+  public class NodeDefinitionSectionHandler extends SectionHandler {
+    private ScriptedProducerNodeDefinition nodeDefinition;
+    private ProducerNode definition;
+    private Map stringParameters;
+    private Map nodeParameters;
+    private String name;
+
+    public NodeDefinitionSectionHandler(String aName) {
+      definition = null;
+      nodeParameters = null;
+      stringParameters = null;
+      name = aName;
+    }
+
+    public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc {
+      if (aTag.equals("parameters")) {
+        if (!anAttributes.isEmpty()) {
+          throw new ProducerConfigExc( "No attributes allowed for tag 'parameters'" );
+        }
+        if (nodeParameters!=null) {
+          throw new ProducerConfigExc( "Parameters have already been declared" );
+        }
+        if (definition!=null) {
+          throw new ProducerConfigExc( "Parameters should come before definition" );
+        }
+
+        return new NodeDefinitionParametersSectionHandler();
+      }
+      else if (aTag.equals("definition")) {
+        return new ProducerNodeSectionHandler(name, nodeParameters.keySet());
+      }
+      else throw new ProducerConfigExc("Only 'verb' tags allowed here, '" + aTag + "' encountered.");
+    }
+
+    public void endElement(SectionHandler aHandler) {
+      if (aHandler instanceof NodeDefinitionParametersSectionHandler) {
+        stringParameters = ((NodeDefinitionParametersSectionHandler) aHandler).getStringParameters();
+        nodeParameters = ((NodeDefinitionParametersSectionHandler) aHandler).getNodeParameters();
+      }
+      else if (aHandler instanceof ProducerNodeSectionHandler) {
+        definition = ((ProducerNodeSectionHandler) aHandler).getProducerNode();
+      }
+    }
+
+    public void finishSection() throws ProducerConfigExc {
+      Iterator i;
+      if (definition == null)
+        throw new ProducerConfigExc( "Definition missing" );
+
+      nodeDefinition = new ScriptedProducerNodeDefinition(name);
+
+      i = nodeParameters.keySet().iterator();
+      while (i.hasNext()) {
+        nodeDefinition.addNodeParameter((String) i.next());
+      }
+
+      i = stringParameters.entrySet().iterator();
+      while (i.hasNext()) {
+        Map.Entry entry = (Map.Entry) i.next();
+        nodeDefinition.addParameter((String) entry.getKey(), (String) entry.getValue());
+      }
+    }
+
+    public ScriptedProducerNodeDefinition getDefinition() {
+      return nodeDefinition;
+    }
+  }
+
+  private final static String   NODE_DEFINITION_PARAMETER_NAME_ATTRIBUTE = "name";
+  private final static String   NODE_DEFINITION_PARAMETER_DEFAULTVALUE_ATTRIBUTE = "defaultvalue";
+  private final static String[] NODE_DEFINITION_PARAMETER_REQUIRED_ATTRIBUTES = { NODE_DEFINITION_PARAMETER_NAME_ATTRIBUTE };
+  private final static String[] NODE_DEFINITION_PARAMETER_OPTIONAL_ATTRIBUTES = { NODE_DEFINITION_PARAMETER_DEFAULTVALUE_ATTRIBUTE };
+  private final static String[] NODE_DEFINITION_NODE_PARAMETER_OPTIONAL_ATTRIBUTES = { };
+
+  public class NodeDefinitionParametersSectionHandler extends SectionHandler {
+    private Map nodeParameters;
+    private Map stringParameters;
+
+    public NodeDefinitionParametersSectionHandler() {
+      nodeParameters = new HashMap();
+      stringParameters = new HashMap();
+    }
+
+    public SectionHandler startElement(String aTag, Map anAttributes) throws ProducerConfigExc {
+      String parameterName;
+      String defaultValue;
+
+      if (aTag.equals("node")) {
+        ReaderTool.checkAttributes(anAttributes, NODE_DEFINITION_PARAMETER_REQUIRED_ATTRIBUTES, NODE_DEFINITION_NODE_PARAMETER_OPTIONAL_ATTRIBUTES);
+        parameterName = (String) anAttributes.get( NODE_DEFINITION_PARAMETER_NAME_ATTRIBUTE );
+
+        if (nodeParameters.containsKey(parameterName))
+          throw new ProducerConfigExc("Duplicate parameter name: '" + parameterName + "'");
+
+        ReaderTool.checkValidIdentifier( parameterName );
+
+        nodeParameters.put(parameterName, parameterName);
+
+        return new EmptySectionHandler();
+      }
+      else if (aTag.equals("string")) {
+        ReaderTool.checkAttributes(anAttributes, NODE_DEFINITION_PARAMETER_REQUIRED_ATTRIBUTES, NODE_DEFINITION_PARAMETER_OPTIONAL_ATTRIBUTES);
+        parameterName = (String) anAttributes.get( NODE_DEFINITION_PARAMETER_NAME_ATTRIBUTE );
+
+        if (stringParameters.containsKey(parameterName))
+          throw new ProducerConfigExc("Duplicate parameter name: '" + parameterName + "'");
+
+        ReaderTool.checkValidIdentifier( parameterName );
+
+        defaultValue = (String) anAttributes.get( NODE_DEFINITION_PARAMETER_DEFAULTVALUE_ATTRIBUTE );
+
+        stringParameters.put(parameterName, defaultValue);
+
+        return new EmptySectionHandler();
+      }
+      else throw new ProducerConfigExc("Only 'string' and 'node' tags allowed here, '" + aTag + "' encountered.");
+
+    }
+
+    public void endElement(SectionHandler aHandler) {
+    }
+
+    public void finishSection() {
+    }
+
+    public Map getNodeParameters() {
+      return nodeParameters;
+    }
+
+    public Map getStringParameters() {
+      return stringParameters;
+    }
+  }
+}
+
+
+/*
+ /                 (expecting producers)
+ producers/        (expecting nodedefinition, producer)
+   nodedefinition  (expecting parameters, definition)
+     parameters    (expecting parameter declarations)
+     definition    (expecting nodes, subnodes)
+*/
+
diff --git a/source/mir/producer/reader/ProducerNodeBuilder.java b/source/mir/producer/reader/ProducerNodeBuilder.java
new file mode 100755 (executable)
index 0000000..c298761
--- /dev/null
@@ -0,0 +1,34 @@
+package mir.producer.reader;
+
+import java.util.*;
+import mir.producer.*;
+
+public interface ProducerNodeBuilder  {
+  public void setAttributes(Map anAttributes) throws ProducerConfigExc;
+  public void setSubNode(String aName, ProducerNode aNode) throws ProducerConfigExc;
+  public Set getAvailableSubNodes() throws ProducerConfigExc;
+  public ProducerNode constructNode() throws ProducerConfigExc;
+
+  public interface ProducerNodeBuilderFactory {
+    public ProducerNodeBuilder makeBuilder() throws ProducerConfigExc;
+  }
+
+  public class DefaultProducerNodeBuilderFactory implements ProducerNodeBuilderFactory {
+    private Class producerNodeBuilderClass;
+
+    public DefaultProducerNodeBuilderFactory(Class aProducerNodeBuilderClass) {
+      producerNodeBuilderClass = aProducerNodeBuilderClass;
+    }
+
+    public ProducerNodeBuilder makeBuilder() throws ProducerConfigExc{
+      try {
+        return (ProducerNodeBuilder) producerNodeBuilderClass.newInstance();
+      }
+      catch (Throwable t) {
+        throw new ProducerConfigFailure(t);
+      }
+    }
+  }
+}
+
+
diff --git a/source/mir/producer/reader/ProducerNodeBuilderLibrary.java b/source/mir/producer/reader/ProducerNodeBuilderLibrary.java
new file mode 100755 (executable)
index 0000000..4a7be95
--- /dev/null
@@ -0,0 +1,31 @@
+package mir.producer.reader;
+
+import java.util.*;
+import mir.producer.*;
+
+public class ProducerNodeBuilderLibrary {
+  private Map nodeBuilders;
+
+  public ProducerNodeBuilderLibrary() {
+    nodeBuilders = new HashMap();
+  }
+
+  public void registerBuilder(String aName, Class aProducerNodeBuilderClass) {
+    registerFactory(aName, new ProducerNodeBuilder.DefaultProducerNodeBuilderFactory( aProducerNodeBuilderClass ));
+  }
+
+  public void registerFactory(String aName, ProducerNodeBuilder.ProducerNodeBuilderFactory aFactory) {
+    nodeBuilders.put(aName, aFactory);
+  }
+
+  public boolean hasBuilderForName(String aName) {
+    return nodeBuilders.containsKey(aName);
+  }
+
+  public ProducerNodeBuilder constructBuilder(String aName) throws ProducerConfigExc {
+    if (hasBuilderForName(aName))
+      return ((ProducerNodeBuilder.ProducerNodeBuilderFactory) nodeBuilders.get(aName)).makeBuilder();
+    else
+      throw new ProducerConfigExc("ProducerNodeBuilder: no builder with name '" + aName + "' found.");
+  }
+}
diff --git a/source/mir/producer/reader/ReaderTool.java b/source/mir/producer/reader/ReaderTool.java
new file mode 100755 (executable)
index 0000000..376196f
--- /dev/null
@@ -0,0 +1,63 @@
+package mir.producer.reader;
+
+import java.util.*;
+
+public class ReaderTool {
+
+  public static void checkValidIdentifier(String anIdentifier) throws ProducerConfigExc {
+  }
+
+  public static String getStringAttributeWithDefault(Map anAttributes, String aKey, String aDefault) {
+    if (anAttributes.containsKey(aKey))
+      return (String) anAttributes.get(aKey);
+    else
+      return aDefault;
+  }
+
+  public static void checkIntegerAttribute(Map anAttributes, String aKey) throws ProducerConfigExc {
+    try {
+      Integer.parseInt((String) anAttributes.get(aKey));
+    }
+    catch (Throwable t) {
+      throw new ProducerConfigExc("attribute '"+aKey+"' is not an integer" );
+    }
+  }
+
+  public static int getIntegerAttributeWithDefault(Map anAttributes, String aKey, int aDefault) throws ProducerConfigExc  {
+    String value;
+
+    if (anAttributes.containsKey(aKey)) {
+      checkIntegerAttribute(anAttributes, aKey);
+      return Integer.parseInt((String) anAttributes.get(aKey));
+    }
+    else
+      return aDefault;
+  }
+
+  public static void checkAttributes(Map anAttributes, String[] aRequiredAttributes, String[] anOptionalAttributes)  throws ProducerConfigExc {
+    checkAttributeSet(anAttributes.keySet(),
+       new HashSet(Arrays.asList(aRequiredAttributes)),
+       new HashSet(Arrays.asList(anOptionalAttributes)));
+  }
+
+  public static void checkAttributeSet(Set aSet, Set aRequiredElements, Set anOptionalElements) throws ProducerConfigExc{
+    Iterator i;
+
+    i = aSet.iterator();
+    while (i.hasNext()) {
+      Object item = i.next();
+
+      if (!(aRequiredElements.contains(item) || anOptionalElements.contains(item)))
+        throw new ProducerConfigExc("unknown attribute '" + item + "'" );
+    }
+
+    i = aRequiredElements.iterator();
+    while (i.hasNext()) {
+      Object item = i.next();
+
+      if (!(aSet.contains(item)))
+        throw new ProducerConfigExc("missing required attribute '" + item + "'" );
+    }
+
+  }
+}
\ No newline at end of file
diff --git a/source/mir/producer/reader/ScriptedProducerFactory.java b/source/mir/producer/reader/ScriptedProducerFactory.java
new file mode 100755 (executable)
index 0000000..985686e
--- /dev/null
@@ -0,0 +1,43 @@
+package mir.producer.reader;
+
+import java.util.*;
+import mir.producer.*;
+
+public class ScriptedProducerFactory implements ProducerFactory {
+  private Map verbs;
+  private ProducerNode body;
+  private String defaultVerb;
+
+  public ScriptedProducerFactory(Map aVerbs, ProducerNode aBody, String aDefaultVerb) {
+    verbs = aVerbs;
+    body = aBody;
+    defaultVerb = aDefaultVerb;
+  }
+
+  public ScriptedProducerFactory(Map aVerbs, ProducerNode aBody) {
+    this(aVerbs, aBody, null);
+  }
+
+  public Producer makeProducer(String aVerb, Map aStartingValues) throws ProducerFailure, ProducerExc {
+    CompositeProducerNode rootNode;
+    ProducerNode verbNode;
+
+    if (verbs.containsKey(aVerb)) {
+      verbNode = (ProducerNode) verbs.get(aVerb);
+    }
+    else if (defaultVerb!=null && verbs.containsKey(defaultVerb)) {
+      verbNode = (ProducerNode) verbs.get(defaultVerb);
+    }
+    else throw new ProducerExc("Undefined verb: " + aVerb);
+
+    rootNode = new CompositeProducerNode();
+    rootNode.addSubNode(verbNode);
+    rootNode.addSubNode(body);
+
+    return new NodedProducer(rootNode, aVerb, aStartingValues);
+  };
+
+  public Iterator verbs() {
+    return verbs.keySet().iterator();
+  }
+}
\ No newline at end of file
diff --git a/source/mir/producer/reader/ScriptedProducerNode.java b/source/mir/producer/reader/ScriptedProducerNode.java
new file mode 100755 (executable)
index 0000000..08925cb
--- /dev/null
@@ -0,0 +1,56 @@
+package mir.producer.reader;
+
+import java.util.*;
+import java.io.*;
+import mir.producer.*;
+import mir.util.*;
+
+public class ScriptedProducerNode implements ProducerNode {
+  private ScriptedProducerNodeDefinition definition;
+  private Map parameterValues;
+  private Map nodeParameterValues;
+
+  public ScriptedProducerNode(ScriptedProducerNodeDefinition aDefinition, Map aParameterValues, Map aNodeParameterValues) {
+    definition = aDefinition;
+    parameterValues = new HashMap();
+    parameterValues.putAll(aParameterValues);
+    nodeParameterValues = new HashMap();
+    nodeParameterValues.putAll(aNodeParameterValues);
+  }
+
+  public Set buildVerbSet() {
+    return new HashSet();
+  }
+
+  public void produce(Map aValues, String aVerb, PrintWriter aLogger) throws ProducerFailure, ProducerExc {
+    try {
+      Map oldValues = ScriptedProducerNodeTool.saveMapValues(aValues, definition.getParameters().keySet());
+      try {
+        Iterator i = parameterValues.entrySet().iterator();
+
+        while (i.hasNext()) {
+          Map.Entry entry = (Map.Entry) i.next();
+
+          if (entry.getValue() instanceof String) {
+            aValues.put(entry.getKey(), ParameterExpander.expandExpression(aValues, (String) entry.getValue()));
+          }
+        }
+
+        ScriptedProducerNodeTool.pushNodeParameterValues(aValues, definition.getName(), nodeParameterValues);
+        try {
+          definition.getBody().produce(aValues, aVerb, aLogger);
+        }
+        finally {
+          ScriptedProducerNodeTool.popNodeParameterValues(aValues, definition.getName());
+        }
+      }
+      finally {
+        ScriptedProducerNodeTool.restoreMapValues(aValues, definition.getParameters().keySet(), oldValues);
+      }
+    }
+    catch (Exception e) {
+      throw new ProducerFailure(e);
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/source/mir/producer/reader/ScriptedProducerNodeDefinition.java b/source/mir/producer/reader/ScriptedProducerNodeDefinition.java
new file mode 100755 (executable)
index 0000000..b9d9ec2
--- /dev/null
@@ -0,0 +1,100 @@
+package mir.producer.reader;
+
+import java.util.*;
+import java.io.*;
+import mir.producer.*;
+
+public class ScriptedProducerNodeDefinition {
+  private Map parameters;               // name -> default value
+  private Set nodeParameters;
+  private ProducerNode body;
+  private String name;
+
+  public static String SCRIPTED_PRODUCERNODE_RUNTIMEDATA_KEY = "$SCRIPTRUNTIMEDATA";
+  public static String SCRIPTED_PRODUCERNODE_RUNTIMESTACK_KEY = "stack";
+
+  public ScriptedProducerNodeDefinition(String aName) {
+    name = aName;
+    parameters = new HashMap();
+    nodeParameters = new HashSet();
+    body = new CompositeProducerNode();
+  }
+
+  public void addParameter(String aName, String aDefaultValue) {
+    parameters.put(aName, aDefaultValue);
+  }
+
+  public void addNodeParameter(String aName) {
+    nodeParameters.add(aName);
+  }
+
+  public void setBody(ProducerNode aBody) {
+    body = aBody;
+  }
+
+  protected Map getParameters() {
+    return parameters;
+  }
+
+  protected Set getNodeParameters() {
+    return nodeParameters;
+  }
+
+  protected ProducerNode getBody() {
+    return body;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public Set getRequiredAttributes() {
+    return getAttributesSelection(true);
+  }
+
+  public Set getOptionalAttributes() {
+    return getAttributesSelection(false);
+  }
+
+  public Set getAttributesSelection(boolean aRequired) {
+    Set result = new HashSet();
+    Iterator i = parameters.entrySet().iterator();
+
+    while (i.hasNext()) {
+      Map.Entry entry = (Map.Entry) i.next();
+
+      if (entry.getValue() == null && aRequired ) {
+        result.add(entry.getKey());
+      }
+    }
+
+    return result;
+  }
+
+
+  protected static class NodeParameterProducerNode implements ProducerNode {
+    private String parameterName;
+    private String definitionName;
+
+    public NodeParameterProducerNode(String aDefinitionName, String aParameterName) {
+      definitionName = aDefinitionName;
+      parameterName = aParameterName;
+    }
+
+    public void produce(Map aValues, String aVerb, PrintWriter aLogger) throws ProducerExc, ProducerFailure {
+      ProducerNode producerNode;
+
+      Map runTimeData = (Map) ((Map) aValues.get(SCRIPTED_PRODUCERNODE_RUNTIMEDATA_KEY)).get(definitionName);
+      Map parameters = (Map) ((Stack) runTimeData.get( SCRIPTED_PRODUCERNODE_RUNTIMESTACK_KEY )).peek();
+
+      producerNode = (ProducerNode) parameters.get(parameterName);
+
+      if (producerNode != null)
+        producerNode.produce(aValues, aVerb, aLogger);
+    }
+
+    public Set buildVerbSet() {
+      return new HashSet();
+    }
+  }
+}
\ No newline at end of file
diff --git a/source/mir/producer/reader/ScriptedProducerNodeTool.java b/source/mir/producer/reader/ScriptedProducerNodeTool.java
new file mode 100755 (executable)
index 0000000..76c7fa5
--- /dev/null
@@ -0,0 +1,56 @@
+package mir.producer.reader;
+
+import java.util.*;
+
+public class ScriptedProducerNodeTool {
+
+  public static Object getOrMakeMapValueForKey(Map aMap, Object aKey, Object aDefaultValue) {
+    if (aMap.containsKey(aKey))
+      return aMap.get(aKey);
+    else {
+      aMap.put(aKey, aDefaultValue);
+      return aDefaultValue;
+    }
+  }
+
+  public static Stack getRunTimeStack(Map aProductionMap, String aDefinitionName) {
+    Map runtimeData = (Map) getOrMakeMapValueForKey(aProductionMap, ScriptedProducerNodeDefinition.SCRIPTED_PRODUCERNODE_RUNTIMEDATA_KEY, new HashMap());
+    runtimeData = (Map) getOrMakeMapValueForKey(runtimeData, aDefinitionName, new HashMap());
+    return (Stack) getOrMakeMapValueForKey(runtimeData, ScriptedProducerNodeDefinition.SCRIPTED_PRODUCERNODE_RUNTIMESTACK_KEY, new Stack());
+  }
+
+  public static void pushNodeParameterValues(Map aProductionMap, String aDefinitionName, Map aNodeParameterValues) {
+    Stack runtimeStack = getRunTimeStack(aProductionMap, aDefinitionName);
+    runtimeStack.push(aNodeParameterValues);
+  }
+
+  public static void popNodeParameterValues(Map aProductionMap, String aDefinitionName) {
+    Stack runtimeStack = getRunTimeStack(aProductionMap, aDefinitionName);
+    runtimeStack.pop();
+  }
+
+  public static Map saveMapValues(Map aMap, Set aKeys) {
+    Map result = new HashMap();
+    Iterator i = aKeys.iterator();
+
+    while (i.hasNext()) {
+      Object key = i.next();
+      if (aMap.containsKey(key))
+        result.put(key, aMap.get(key));
+    }
+
+    return result;
+  }
+
+  public static void restoreMapValues(Map aMap, Set aKeys, Map aSavedValues) {
+    Iterator i = aKeys.iterator();
+
+    while (i.hasNext()) {
+      Object key = i.next();
+      if (aSavedValues.containsKey(key))
+        aMap.put(key, aSavedValues.get(key));
+      else
+        aMap.remove(key);
+    }
+  }
+}
\ No newline at end of file
diff --git a/source/mir/util/FileMonitor.java b/source/mir/util/FileMonitor.java
new file mode 100755 (executable)
index 0000000..a35dc67
--- /dev/null
@@ -0,0 +1,35 @@
+package mir.util;
+
+import java.util.*;
+import java.io.*;
+
+public class FileMonitor {
+  private Map files;
+
+  public FileMonitor() {
+    files = new HashMap();
+  }
+
+  public void addFile(File aFile) {
+    files.put(aFile, new Long(aFile.lastModified()));
+  }
+
+  public void clear() {
+    files.clear();
+  }
+
+  public boolean hasChanged() {
+    Iterator i = files.entrySet().iterator();
+
+    while (i.hasNext()) {
+      Map.Entry entry = (Map.Entry) i.next();
+      File file = (File) entry.getKey();
+      Long lastModified = (Long) entry.getValue();
+
+      if (lastModified.longValue()!=file.lastModified())
+        return true;
+    }
+
+    return false;
+  }
+}
\ No newline at end of file
index c377fc3..acebf0c 100755 (executable)
@@ -6,8 +6,6 @@ import multex.Exc;
 import java.util.*;
 
 public class ParameterExpander {
-
-  final static String DEFAULT_KEY = "(default)";
   final static String NODE_SEPARATOR = ".";
 
   public static List splitString(String aString, String aSeparator) {
@@ -26,31 +24,37 @@ public class ParameterExpander {
     return result;
   }
 
-  private static Object findNode(String aKey, Map aMap, List aParts) throws Exception {
+  private static Object findNode(String aKey, Map aMap, List aParts, boolean aMakeIfNotPresent) throws Exception {
     Iterator i;
     String location = "";
     Object node = aMap;
+    Object newNode;
 
     i = aParts.iterator();
 
-    while(i.hasNext()) {
+    while (i.hasNext()) {
       String part = (String) i.next();
 
       if (!(node instanceof Map)) {
         throw new Exception( "Can't expand key " + aKey + ": " + location + " is not a map" );
       }
 
-      node = ((Map) node).get(part);
-
       if (location.length()>0) {
         location=location + NODE_SEPARATOR;
       }
-
       location = location + part;
 
-      if (node == null) {
-        throw new ParameterExpanderExc( "Can't expand key {1}: {2} does not exist", new Object[]{aKey,location} );
-      }
+      newNode = ((Map) node).get(part);
+
+      if (newNode == null)
+        if (aMakeIfNotPresent) {
+          newNode = new HashMap();
+          ((Map) node).put(part, newNode);
+        }
+        else
+          throw new ParameterExpanderExc( "Can't expand key {1}: {2} does not exist", new Object[]{aKey,location} );
+
+      node = newNode;
     }
 
     return node;
@@ -60,7 +64,7 @@ public class ParameterExpander {
     Object node;
     List parts = splitString(aKey, NODE_SEPARATOR);
 
-    node = findNode(aKey, aMap, parts);
+    node = findNode(aKey, aMap, parts, false);
 
     return node;
   }
@@ -80,7 +84,7 @@ public class ParameterExpander {
     String key = (String) parts.get(parts.size()-1);
     parts.remove(parts.size()-1);
 
-    Object node=findNode(aKey, aMap, parts);
+    Object node=findNode(aKey, aMap, parts, true);
 
     if (node instanceof Map) {
       ((Map) node).put(key, aValue);
@@ -126,12 +130,643 @@ public class ParameterExpander {
     return result.toString();
   }
 
+  public static boolean evaluateBooleanExpression(Map aMap, String anExpression) throws Exception {
+    Parser parser = new Parser(anExpression, aMap);
+
+    return parser.parseBoolean();
+  }
+
+  public static Object evaluateExpression(Map aMap, String anExpression) throws Exception {
+    Parser parser = new Parser(anExpression, aMap);
+
+    return parser.parseWhole();
+  }
+
+  private static class Reader {
+    private String data;
+    private int position;
+
+    public Reader(String aData) {
+      data = aData;
+      position=0;
+    }
+
+    public Character peek() {
+      if (position<data.length()) {
+        return (new Character(data.charAt(position)));
+      }
+
+      return null;
+    }
+
+    public boolean hasNext() {
+      return peek()!=null;
+    }
+
+    public Character getNext() {
+      Character result = peek();
+
+      if (result!=null)
+        position++;
+
+      return result;
+    }
+
+    public int getPosition() {
+      return position;
+    }
+  }
+
+  private static abstract class Token {
+  }
+
+  public static abstract class PunctuationToken extends Token { public PunctuationToken() { }; }
+    private static class LeftSquareBraceToken extends PunctuationToken {};
+    private static class RightSquareBraceToken extends PunctuationToken {};
+    private static class ANDToken extends PunctuationToken {};
+    private static class ORToken extends PunctuationToken {};
+    private static class EqualsToken extends PunctuationToken {};
+    private static class EqualsNotToken extends PunctuationToken {};
+    private static class NOTToken extends PunctuationToken {};
+    private static class LeftParenthesisToken extends PunctuationToken {};
+    private static class RightParenthesisToken extends PunctuationToken {};
+    private static class CommaToken extends PunctuationToken {};
+    private static class PeriodToken extends PunctuationToken {};
+    private static class PlusToken extends PunctuationToken {};
+    private static class TimesToken extends PunctuationToken {};
+    private static class DivideToken extends PunctuationToken {};
+    private static class MinusToken extends PunctuationToken {};
+    private static class ConcatenateToken extends PunctuationToken {};
+    private static class LessThanOrEqualsToken extends PunctuationToken {};
+    private static class GreaterThanOrEqualsToken extends PunctuationToken {};
+    private static class LessThanToken extends PunctuationToken {};
+    private static class GreaterThanToken extends PunctuationToken {};
+
+
+  private static class IdentifierToken extends Token {
+    private String name;
+
+    public IdentifierToken(String aName) {
+      name = aName;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+  }
+
+  private static class LiteralToken extends Token {
+    private Object value;
+
+    public LiteralToken(Object aValue) {
+      value = aValue;
+    }
+
+    public Object getValue() {
+      return value;
+    }
+  }
+
+  private static class Scanner {
+    private Reader reader;
+    private Token nextToken;
+
+    public Scanner(Reader aReader) {
+      reader = aReader;
+    }
+
+    public Token scanStringLiteral() {
+      StringBuffer result = new StringBuffer();
+      Character delimiter;
+
+      delimiter = reader.getNext();
+
+      while (reader.hasNext() && !reader.peek().equals(delimiter)) {
+        result.append(reader.getNext());
+      }
+
+      if (!reader.hasNext())
+        throw new RuntimeException("unterminated string");
+      else
+        reader.getNext();
+
+      return new LiteralToken(result.toString());
+    }
+
+    public int getPosition() {
+      return reader.getPosition();
+    }
+
+    private Token scanNumber() {
+      StringBuffer result = new StringBuffer();
+      result.append(reader.getNext());
+
+      while (reader.hasNext() && isNumberRest(reader.peek().charValue())) {
+        result.append(reader.getNext());
+      }
+
+      try {
+        return new LiteralToken(Integer.getInteger(result.toString()));
+      }
+      catch (NumberFormatException e) {
+        throw new RuntimeException("Invalid number: " + e.getMessage());
+      }
+    }
+
+    private Token scanIdentifierKeyword() {
+      StringBuffer result = new StringBuffer();
+      result.append(reader.getNext());
+
+      while (reader.hasNext() && isIdentifierRest(reader.peek().charValue())) {
+        result.append(reader.getNext());
+      }
+
+      return new IdentifierToken(result.toString());
+    }
+
+    private Token scanPunctuation() {
+      Character c;
+
+      c = reader.getNext();
+
+      switch(c.charValue()) {
+        case '[': return new LeftSquareBraceToken();
+        case ']': return new RightSquareBraceToken();
+        case '&':
+          if (reader.hasNext() && reader.peek().charValue() == '&') {
+            reader.getNext();
+            return new ANDToken();
+          }
+          else {
+            return new ConcatenateToken();
+          }
+        case '|':
+          if (reader.hasNext() && reader.peek().charValue() == '|') {
+            reader.getNext();
+            return new ORToken();
+          }
+          else {
+            throw new RuntimeException("Unknown character: '|'");
+          }
+
+        case '=':
+          if (reader.hasNext() && reader.peek().charValue() == '=') {
+            reader.getNext();
+            return new EqualsToken();
+          }
+          else {
+            throw new RuntimeException("Unknown character: '='");
+          }
+
+        case '!':
+          if (reader.hasNext() && reader.peek().charValue() == '=') {
+            reader.getNext();
+            return new EqualsNotToken();
+          }
+          else {
+            return new NOTToken();
+          }
+
+        case '(': return new LeftParenthesisToken ();
+
+        case ')': return new RightParenthesisToken ();
+        case ',': return new CommaToken ();
+        case '.': return new PeriodToken ();
+        case '+': return new PlusToken ();
+        case '*': return new TimesToken ();
+        case '/': return new DivideToken ();
+        case '-': return new MinusToken ();
+        case '<':
+          if (reader.hasNext() && reader.peek().charValue() == '=') {
+            reader.getNext();
+            return new LessThanOrEqualsToken();
+          }
+          else {
+            return new LessThanToken();
+          }
+
+        case '>':
+          if (reader.hasNext() && reader.peek().charValue() == '=') {
+            reader.getNext();
+            return new GreaterThanOrEqualsToken();
+          }
+          else {
+            return new GreaterThanToken();
+          }
+        default:
+          throw new RuntimeException("Unexpected character: "+c);
+      }
+    }
+
+    public void skipWhitespace() {
+      while (reader.hasNext() && Character.isWhitespace(reader.peek().charValue()))
+        reader.getNext();
+    };
+
+    private boolean isIdentifierStart(char c) {
+      return Character.isLetter(c) || (c == '_');
+    }
+
+    private boolean isIdentifierRest(char c) {
+      return Character.isLetterOrDigit(c) || (c == '_');
+    }
+
+    private boolean isNumberStart(char c) {
+      return Character.isDigit(c);
+    }
+
+    private boolean isNumberRest(char c) {
+      return Character.isDigit(c);
+    }
+
+    public Token scanNext() {
+      skipWhitespace();
+
+      if (!reader.hasNext())
+        return null;
+
+      Character c = reader.peek();
+
+      switch(c.charValue()) {
+        case '\'':
+        case '"': return scanStringLiteral();
+
+        default: {
+          if (isIdentifierStart(c.charValue())) {
+            return scanIdentifierKeyword();
+          }
+          else if (isNumberStart(c.charValue())) {
+            return scanNumber();
+          }
+          else
+            return scanPunctuation();
+        }
+      }
+    }
+
+    public Token scan() {
+      Token result = null;
+
+      if (nextToken!=null) {
+        result = nextToken;
+      }
+      else {
+        result = scanNext();
+      }
+
+      nextToken=null;
+
+      return result;
+    }
+
+    public Token peek() {
+      nextToken = scan();
+
+      return nextToken;
+    }
+
+    public boolean hasToken() {
+      return peek()!=null;
+    }
+  }
+
+  private static class Parser {
+    private Scanner scanner;
+    private Map valueMap;
+
+    public Parser(String anExpression, Map aValueMap) {
+      scanner = new Scanner(new Reader(anExpression));
+      valueMap = aValueMap;
+    }
+
+    public boolean parseBoolean() {
+      try {
+        return interpretAsBoolean(parseWhole());
+      }
+      catch (Throwable t) {
+        t.printStackTrace(System.out);
+        throw new RuntimeException("Parser error at position "+getLocation()+":"+t.getMessage());
+      }
+    }
+
+    public String parseString() {
+      try {
+        return interpretAsString(parseWhole());
+      }
+      catch (Throwable t) {
+        throw new RuntimeException("Parser error at position "+getLocation()+":"+t.getMessage());
+      }
+    }
+
+    private String getLocation() {
+      int position = scanner.getPosition();
+
+      return Integer.toString(position);
+    }
+
+    private Object parseWhole() {
+      Object result = parse();
+
+      if (scanner.hasToken()) {
+        throw new RuntimeException("Operator expected");
+      }
+
+      return result;
+    }
+
+    private Object parse() {
+      return parseUntil(MAX_OPERATOR_LEVEL);
+    }
+
+    private Object parseSet() {
+      Token token;
+      Object expression;
+      Set result = new HashSet();
+
+      token = scanner.scan();
+      if (!(token instanceof LeftParenthesisToken)) {
+        throw new RuntimeException("( expected after in keyword");
+      }
+
+      if (scanner.peek() instanceof RightParenthesisToken) {
+        scanner.scan();
+        return result;
+      }
+
+      do {
+        expression = parse();
+
+        if (expression==null) {
+          throw new RuntimeException("expression expected");
+        }
+
+        result.add(expression);
+
+        token = scanner.scan();
+      } while (token instanceof CommaToken);
+
+      if (!(token instanceof RightParenthesisToken)) {
+        throw new RuntimeException(") or , expected");
+      }
+
+      return result;
+    }
+
+    private Object parseVariable() {
+      boolean done;
+      Token token;
+      Object currentValue = valueMap;
+      Object qualifier;
+
+      do {
+        token = scanner.scan();
+        if (token instanceof LeftSquareBraceToken) {
+          qualifier = parseUntil(MAX_OPERATOR_LEVEL);
+          token = scanner.scan();
+          if (!(token instanceof RightSquareBraceToken))
+            throw new RuntimeException("] expected");
+        }
+        else if (token instanceof IdentifierToken) {
+          qualifier = ((IdentifierToken) token).getName();
+        }
+        else
+          throw new RuntimeException("fieldname or [ expected");
+
+        if (currentValue!=null) {
+          if (currentValue instanceof Map) {
+            currentValue = ((Map) currentValue).get(qualifier);
+          }
+          else {
+            throw new RuntimeException("cannot reference into anything other than a map");
+          }
+        }
+        else {
+          // throw? or allow null.null?
+        }
+
+        if (scanner.peek() instanceof PeriodToken)
+        {
+          scanner.scan();
+          done = false;
+        }
+        else
+          done = true;
+      } while (!done);
+
+      return currentValue;
+    }
+
+
+    private Object parseUntil(int aMaxOperatorLevel) {
+      Token token = scanner.peek();
+      Object value;
+
+      if (token instanceof LeftParenthesisToken) {
+        scanner.scan();
+        value = parse();
+        token = scanner.peek();
+        if (!(token instanceof RightParenthesisToken))
+          throw new RuntimeException(") expected");
+        scanner.scan();
+      }
+      else if (isUnaryOperator(token)) {
+        scanner.scan();
+        value = parseUntil(unaryOperatorLevel(token));
+        value = expandOperatorExpression(token, value);
+      }
+      else if (token instanceof IdentifierToken) {
+        value = parseVariable();
+      }
+      else if (token instanceof LiteralToken) {
+        scanner.scan();
+        value = ((LiteralToken) token).getValue();
+      }
+      else
+        throw new RuntimeException("Expression expected");
+
+      token = scanner.peek();
+
+      while (isBinaryOperator(token) && binaryOperatorLevel(token)<aMaxOperatorLevel) {
+        Object value2;
+        scanner.scan();
+
+        if (isINOperator(token)) {
+          value2 = parseSet();
+        }
+        else {
+          value2 = parseUntil(binaryOperatorLevel(token));
+        }
+
+        value = expandOperatorExpression(token, value, value2);
+
+        token = scanner.peek();
+      }
+
+      return value;
+    }
+
+    private static final int MAX_OPERATOR_LEVEL = 1000;               // && || !
+    private static final int LOGICAL_OPERATOR_LEVEL = 5;               // && || !
+    private static final int COMPARISON_OPERATOR_LEVEL = 4;            // == <= >= in < >
+    private static final int ADDITION_OPERATOR_LEVEL = 3;              // + - &
+    private static final int MULTIPLICATION_OPERATOR_LEVEL = 2;        // * /
+
+    private int unaryOperatorLevel(Token aToken) {
+      if (aToken instanceof NOTToken)
+        return LOGICAL_OPERATOR_LEVEL;
+      else if (aToken instanceof MinusToken)
+        return ADDITION_OPERATOR_LEVEL;
+
+      throw new RuntimeException("Internal error: unknown unary operator: " + aToken.getClass().getName());
+    }
+
+    private boolean isUnaryOperator(Token aToken) {
+      return
+          ((aToken instanceof NOTToken) ||
+           (aToken instanceof MinusToken));
+    }
+
+    private int binaryOperatorLevel(Token aToken) {
+      if ((aToken instanceof ANDToken) ||
+          (aToken instanceof ORToken))
+        return LOGICAL_OPERATOR_LEVEL;
+
+      if ((aToken instanceof EqualsToken) ||
+          (aToken instanceof EqualsNotToken) ||
+          (aToken instanceof LessThanOrEqualsToken) ||
+          (aToken instanceof LessThanToken) ||
+          (aToken instanceof GreaterThanOrEqualsToken) ||
+          (aToken instanceof GreaterThanToken) ||
+          isINOperator(aToken))
+        return COMPARISON_OPERATOR_LEVEL;
+
+      if ((aToken instanceof PlusToken) ||
+          (aToken instanceof ConcatenateToken) ||
+          (aToken instanceof MinusToken))
+        return ADDITION_OPERATOR_LEVEL;
+
+      if ((aToken instanceof TimesToken) ||
+          (aToken instanceof DivideToken))
+        return MULTIPLICATION_OPERATOR_LEVEL;
+
+      throw new RuntimeException("Internal error: unknown binary operator: " + aToken.getClass().getName());
+    }
+
+    private boolean isINOperator(Token aToken) {
+      return (aToken instanceof IdentifierToken && ((IdentifierToken) aToken).getName().equals("in"));
+    }
+
+    private boolean isBinaryOperator(Token aToken) {
+      return
+           (aToken instanceof ANDToken) ||
+           (aToken instanceof ORToken) ||
+           (aToken instanceof EqualsToken) ||
+           (aToken instanceof EqualsNotToken) ||
+           (aToken instanceof PlusToken) ||
+           (aToken instanceof TimesToken) ||
+           (aToken instanceof DivideToken) ||
+           (aToken instanceof MinusToken) ||
+           (aToken instanceof ConcatenateToken) ||
+           (aToken instanceof LessThanOrEqualsToken) ||
+           (aToken instanceof LessThanToken) ||
+           (aToken instanceof GreaterThanOrEqualsToken) ||
+           (aToken instanceof GreaterThanToken) ||
+           isINOperator(aToken);
+    }
+
+    private boolean interpretAsBoolean(Object aValue) {
+      if (aValue instanceof Boolean)
+        return ((Boolean) aValue).booleanValue();
+
+      return aValue!=null;
+    }
+
+    private int interpretAsInteger(Object aValue) {
+      if (aValue instanceof Integer)
+        return ((Integer) aValue).intValue();
+
+      throw new RuntimeException("Not an integer");
+    }
+
+    private String interpretAsString(Object aValue) {
+      if (aValue instanceof String)
+        return (String) aValue;
+      if (aValue instanceof Integer)
+        return ((Integer) aValue).toString();
+
+      throw new RuntimeException("Not a string");
+    }
+
+    private Object expandOperatorExpression(Token aToken, Object aValue) {
+      if (aToken instanceof NOTToken)
+        return new Boolean(!interpretAsBoolean(aValue));
+      else if (aToken instanceof MinusToken)
+        return new Integer(-interpretAsInteger(aValue));
+
+      throw new RuntimeException("Internal error: unknown unary operator: " + aToken.getClass().getName());
+    }
+
+    private boolean areEqual(Object aValue1, Object aValue2) {
+      if (aValue1==null || aValue2==null)
+        return (aValue1==null) && (aValue2==null);
+      else
+        return aValue1.equals(aValue2);
+    }
+
+    private Object expandOperatorExpression(Token aToken, Object aValue1, Object aValue2) {
+      if (isINOperator(aToken)) {
+        if (!(aValue2 instanceof Set)) {
+          throw new RuntimeException("Internal error: set expected");
+        }
+
+        Iterator i = ((Set) aValue2).iterator();
+
+        while (i.hasNext()) {
+          if (areEqual(aValue1, i.next()))
+            return Boolean.TRUE;
+        }
+
+        return Boolean.FALSE;
+      }
+
+      if (aToken instanceof ANDToken)
+        return new Boolean(interpretAsBoolean(aValue1) && interpretAsBoolean(aValue2));
+      if (aToken instanceof ORToken)
+        return new Boolean(interpretAsBoolean(aValue1) || interpretAsBoolean(aValue2));
+      if (aToken instanceof EqualsToken) {
+        return new Boolean(areEqual(aValue1, aValue2));
+      }
+      if (aToken instanceof EqualsNotToken)
+        return new Boolean(!areEqual(aValue1, aValue2));
+      if (aToken instanceof PlusToken)
+        return new Integer(interpretAsInteger(aValue1) + interpretAsInteger(aValue2));
+      if (aToken instanceof TimesToken)
+        return new Integer(interpretAsInteger(aValue1) * interpretAsInteger(aValue2));
+      if (aToken instanceof DivideToken)
+        return new Integer(interpretAsInteger(aValue1) / interpretAsInteger(aValue2));
+      if (aToken instanceof MinusToken)
+        return new Integer(interpretAsInteger(aValue1) - interpretAsInteger(aValue2));
+
+      if (aToken instanceof ConcatenateToken)
+        return interpretAsString(aValue1) + interpretAsString(aValue1);
+
+      if (aToken instanceof LessThanOrEqualsToken)
+        return new Boolean(interpretAsInteger(aValue1) <= interpretAsInteger(aValue2));
+      if (aToken instanceof LessThanToken)
+        return new Boolean(interpretAsInteger(aValue1) < interpretAsInteger(aValue2));
+      if (aToken instanceof GreaterThanOrEqualsToken)
+        return new Boolean(interpretAsInteger(aValue1) >= interpretAsInteger(aValue2));
+      if (aToken instanceof GreaterThanToken)
+        return new Boolean(interpretAsInteger(aValue1) > interpretAsInteger(aValue2));
+
+      throw new RuntimeException("Internal error: unknown binary operator: " + aToken.getClass().getName());
+    }
+  }
 
   public static class ParameterExpanderExc extends Exc {
     public ParameterExpanderExc(String msg, Object[] objects) {
       super(msg, objects);
     }
   }
-}
-
-
+}
\ No newline at end of file
index 19f9496..1fb9e60 100755 (executable)
@@ -18,8 +18,8 @@ public class ProducerEngine {
     producerJobQueue = new JobQueue();
     try {
       RandomAccessFile raFile = (new RandomAccessFile(MirGlobal.getConfigProperty("Home") + "/" + MirGlobal.getConfigProperty("Producer.Logfile"), "rw"));
-                       raFile.seek(raFile.length());
-               log = new PrintWriter(new FileWriter( raFile.getFD()));
+                        raFile.seek(raFile.length());
+                log = new PrintWriter(new FileWriter( raFile.getFD()));
     }
     catch (Exception e) {
 //      throw new ProducerEngineRuntimeExc("Creating PrintWriter log failed",e);
@@ -61,10 +61,11 @@ public class ProducerEngine {
 
   private void produceNow(String aProducerFactory, String aVerb, PrintWriter aLogger) {
     try {
-                 long startTime;
-                 long endTime;
+      long startTime;
+      long endTime;
+      Map startingMap = new HashMap();
 
-                 startTime = System.currentTimeMillis();
+      startTime = System.currentTimeMillis();
 
       aLogger.println("Producing (" + aProducerFactory + "," + aVerb + ")");
 
@@ -73,13 +74,14 @@ public class ProducerEngine {
       if (factory == null )
         throw new Exception("No producer factory '"+aProducerFactory+"' present.");
 
-      Producer producer = factory.makeProducer(aVerb);
+      MirGlobal.localizer().producerAssistant().initializeGenerationValueSet(startingMap);
+      Producer producer = factory.makeProducer(aVerb, startingMap);
 
       producer.produce(aLogger);
 
-                 endTime = System.currentTimeMillis();
+      endTime = System.currentTimeMillis();
 
-                 aLogger.println("Time: " + (endTime-startTime) + " ms<br>");
+      aLogger.println("Time: " + (endTime-startTime) + " ms<br>");
     }
     catch (Throwable e) {
       try {
@@ -104,18 +106,21 @@ public class ProducerEngine {
     public void execute() {
       ProducerFactory factory;
       Producer producer;
-                 long startTime;
-                 long endTime;
+      long startTime;
+      long endTime;
+      Map startingMap = new HashMap();
 
-                 startTime = System.currentTimeMillis();
+      startTime = System.currentTimeMillis();
       log.println("Producing job: "+factoryName+"."+verb);
 
       try {
         factory = (ProducerFactory) MirGlobal.localizer().producers().factories().get( factoryName );
 
         if (factory!=null) {
+          MirGlobal.localizer().producerAssistant().initializeGenerationValueSet(startingMap);
+
           synchronized(factory) {
-            producer = factory.makeProducer(verb);
+            producer = factory.makeProducer(verb, startingMap);
           }
           if (producer!=null) {
             producer.produce(log);
@@ -127,10 +132,10 @@ public class ProducerEngine {
         t.printStackTrace(log);
       }
       log.println("Done producing job: "+factoryName+"."+verb);
-                 endTime = System.currentTimeMillis();
+                  endTime = System.currentTimeMillis();
 
-                 log.println("Time: " + (endTime-startTime) + " ms");
-                 log.flush();
+                  log.println("Time: " + (endTime-startTime) + " ms");
+                  log.flush();
     }
   }
 
index ca7542d..65313d0 100755 (executable)
@@ -12,7 +12,7 @@ public class MirCachingLocalizerDecorator implements MirLocalizer {
     localizer = aLocalizer;
   }
 
-  public MirProducerLocalizer producers() {
+  public MirProducerLocalizer producers() throws MirLocalizerFailure, MirLocalizerException {
     if (producerLocalizer==null) {
       producerLocalizer = localizer.producers();
     }
@@ -20,7 +20,7 @@ public class MirCachingLocalizerDecorator implements MirLocalizer {
     return producerLocalizer;
   }
 
-  public MirGeneratorLocalizer generators() {
+  public MirGeneratorLocalizer generators() throws MirLocalizerFailure, MirLocalizerException {
     if (generatorLocalizer==null) {
       generatorLocalizer = localizer.generators();
     }
@@ -28,7 +28,7 @@ public class MirCachingLocalizerDecorator implements MirLocalizer {
     return generatorLocalizer;
   }
 
-  public MirOpenPostingLocalizer openPostings() {
+  public MirOpenPostingLocalizer openPostings() throws MirLocalizerFailure, MirLocalizerException {
     if (openPostingsLocalizer==null) {
       openPostingsLocalizer = localizer.openPostings();
     }
@@ -36,7 +36,7 @@ public class MirCachingLocalizerDecorator implements MirLocalizer {
     return openPostingsLocalizer;
   }
 
-  public MirProducerAssistantLocalizer producerAssistant() {
+  public MirProducerAssistantLocalizer producerAssistant() throws MirLocalizerFailure, MirLocalizerException {
     if (producerAssistantLocalizer==null) {
       producerAssistantLocalizer = localizer.producerAssistant();
     }
@@ -44,7 +44,7 @@ public class MirCachingLocalizerDecorator implements MirLocalizer {
     return producerAssistantLocalizer;
   }
 
-  public MirDataModelLocalizer dataModel() {
+  public MirDataModelLocalizer dataModel() throws MirLocalizerFailure, MirLocalizerException {
     if (dataModelLocalizer==null) {
       dataModelLocalizer = localizer.dataModel();
     }
index ea2a7ca..a966b42 100755 (executable)
@@ -3,5 +3,6 @@ package mircoders.localizer;
 import mir.generator.*;
 
 public interface MirGeneratorLocalizer {
-  public Generator makeGenerator(String anIdentifier) throws MirLocalizerException, MirLocalizerFailure;
+  public Generator.GeneratorLibrary makeGeneratorLibrary() throws MirLocalizerException, MirLocalizerFailure;
+  public WriterEngine makeWriterEngine() throws MirLocalizerException, MirLocalizerFailure;
 }
\ No newline at end of file
index 014d7f2..c27561d 100755 (executable)
@@ -1,9 +1,9 @@
 package mircoders.localizer;
 
 public interface MirLocalizer {
-  public MirProducerLocalizer producers();
-  public MirOpenPostingLocalizer openPostings();
-  public MirProducerAssistantLocalizer producerAssistant();
-  public MirGeneratorLocalizer generators();
-  public MirDataModelLocalizer dataModel();
+  public MirProducerLocalizer producers() throws MirLocalizerFailure, MirLocalizerException;
+  public MirOpenPostingLocalizer openPostings() throws MirLocalizerFailure, MirLocalizerException;
+  public MirProducerAssistantLocalizer producerAssistant() throws MirLocalizerFailure, MirLocalizerException;
+  public MirGeneratorLocalizer generators() throws MirLocalizerFailure, MirLocalizerException;
+  public MirDataModelLocalizer dataModel() throws MirLocalizerFailure, MirLocalizerException;
 }
\ No newline at end of file
index 71a73ef..c5a99e4 100755 (executable)
@@ -6,4 +6,7 @@ public class MirLocalizerFailure extends Failure {
   public MirLocalizerFailure(String msg, Throwable throwable) {
     super(msg, throwable);
   }
+  public MirLocalizerFailure(Throwable aThrowable) {
+    this(aThrowable.getMessage(), aThrowable);
+  }
 }
index 102e0af..4319fae 100755 (executable)
@@ -5,7 +5,5 @@ import java.io.*;
 
 public interface MirProducerAssistantLocalizer {
   public void initializeGenerationValueSet(Map aValueSet) throws MirLocalizerException, MirLocalizerFailure;
-  public PrintWriter openWriter(String anIdentifier, String anEncoding) throws MirLocalizerException, MirLocalizerFailure;
-  public void closeWriter(PrintWriter aWriter) throws MirLocalizerException, MirLocalizerFailure;
   public String filterText(String aText) throws MirLocalizerException, MirLocalizerFailure;
 }
index 9958c63..587b4b4 100755 (executable)
@@ -2,16 +2,23 @@ package mircoders.localizer.basic;
 
 import freemarker.template.*;
 import mir.misc.*;
+import mir.generator.*;
 import mircoders.localizer.*;
 import mircoders.global.*;
 
-public class MirBasicGeneratorLocalizer extends FreemarkerGeneratorLocalizer {
+public class MirBasicGeneratorLocalizer implements MirGeneratorLocalizer {
   protected static Logfile logger = Logfile.getInstance( MirGlobal.getConfigProperty("Home") + "/" + MirGlobal.getConfigProperty("Mir.Localizer.Logfile"));
+  private String templateRoot;
 
   public MirBasicGeneratorLocalizer (String aTemplateRoot) {
-    super(aTemplateRoot);
+    templateRoot = aTemplateRoot;
   }
 
-  public void prepareTemplate(Template aTemplate) throws MirLocalizerException {
+  public Generator.GeneratorLibrary makeGeneratorLibrary() throws MirLocalizerException, MirLocalizerFailure {
+    return new FreemarkerGenerator.FreemarkerGeneratorLibrary(templateRoot);
+  };
+
+  public WriterEngine makeWriterEngine() throws MirLocalizerException, MirLocalizerFailure {
+    return new MirBasicWriterEngine(MirGlobal.getConfigProperty("Mir.DefaultEncoding"));
   }
 }
index 729e8ef..b16b0e0 100755 (executable)
@@ -7,7 +7,7 @@ import mircoders.localizer.*;
 public class MirBasicLocalizer implements MirLocalizer {
   protected static Logfile logger = Logfile.getInstance( MirGlobal.getConfigProperty("Home") + "/" + MirGlobal.getConfigProperty("Mir.Localizer.Logfile"));
 
-  public MirProducerLocalizer producers() {
+  public MirProducerLocalizer producers() throws MirLocalizerFailure, MirLocalizerException {
     return new MirBasicProducerLocalizer();
   }
 
index 2e296de..550d514 100755 (executable)
@@ -70,38 +70,6 @@ public class MirBasicProducerAssistantLocalizer implements MirProducerAssistantL
     aValueSet.put("articletype", articleTypeMap);
   };
 
-  public PrintWriter openWriter(String anIdentifier, String anEncoding) throws MirLocalizerFailure {
-    String encoding;
-    File file;
-    File dir;
-
-    if (anEncoding!=null && !anEncoding.equals(""))
-      encoding = anEncoding;
-    else
-      encoding = MirGlobal.getConfigProperty("Mir.DefaultEncoding");
-
-    try {
-      file = new File( anIdentifier );
-      dir = new File(file.getParent());
-        if (dir!=null && !dir.exists()){
-          dir.mkdirs();
-      }
-
-      return new PrintWriter(
-        new OutputStreamWriter(
-          new FileOutputStream(file), encoding
-        )
-      );
-    }
-    catch (Throwable t) {
-      throw new MirLocalizerFailure("Failure while opening a PrintWriter: "+t.getMessage(),t);
-    }
-  };
-
-  public void closeWriter(PrintWriter aWriter) {
-    aWriter.close();
-  };
-
   public String filterText(String aText) {
     return StringUtil.createHTML(
         StringUtil.deleteForbiddenTags(aText),
index d9a4178..741d0f1 100755 (executable)
@@ -1,34 +1,57 @@
 package mircoders.localizer.basic;
 
 import java.util.*;
+import java.io.*;
 import mir.producer.*;
+import mir.generator.*;
+import mir.producer.reader.*;
 import mir.misc.*;
+import mir.util.*;
 import mir.entity.adapter.*;
 import mircoders.global.*;
 import mircoders.global.*;
 import mircoders.localizer.*;
+import mircoders.producer.reader.*;
 import mircoders.producer.*;
 
 public class MirBasicProducerLocalizer implements MirProducerLocalizer {
   private Map producerFactories;
+  protected FileMonitor fileMonitor;
   protected EntityAdapterModel model;
+  protected Generator.GeneratorLibrary generatorLibrary;
+  protected WriterEngine writerEngine;
+
   protected static Logfile logger = Logfile.getInstance( MirGlobal.getConfigProperty("Home") + "/" + MirGlobal.getConfigProperty("Mir.Localizer.Logfile"));
 
   public MirBasicProducerLocalizer() {
-    producerFactories = new HashMap();
 
     try {
+      producerFactories = new HashMap();
       model = MirGlobal.localizer().dataModel().adapterModel();
+      generatorLibrary = MirGlobal.localizer().generators().makeGeneratorLibrary();
+      writerEngine = MirGlobal.localizer().generators().makeWriterEngine();
     }
     catch (Throwable t) {
       logger.printError("MirBasicProducerLocalizer(): Exception "+t.getMessage());
       model = new EntityAdapterModel();
     }
-
-    setupFactories(producerFactories);
   }
 
-  public Map factories() {
+  public Map factories() throws MirLocalizerException {
+    if (fileMonitor==null || producerFactories == null || fileMonitor.hasChanged()) {
+      try {
+        Map newProducers = new HashMap();
+        FileMonitor newFileMonitor = new FileMonitor();
+        setupFactories(newProducers, newFileMonitor);
+
+        producerFactories = newProducers;
+        fileMonitor = newFileMonitor;
+      }
+      catch (Throwable t) {
+        logger.printError("MirBasicProducerLocalizer.factories(): Unable to setup factories: "+t.getMessage());
+      }
+    }
+
     return producerFactories;
   };
 
@@ -52,36 +75,68 @@ public class MirBasicProducerLocalizer implements MirProducerLocalizer {
   protected void setupTopicsFactory(CompositeProducerNode aProducerNode) {
   }
 
-  protected void setupFactories(Map aFactoriesMap ) {
+  protected void setupProducerNodeBuilderLibrary(ProducerNodeBuilderLibrary aLibrary) {
+    DefaultProducerNodeBuilders.registerBuilders(aLibrary, model, generatorLibrary, writerEngine);
+    SupplementalProducerNodeBuilders.registerBuilders(aLibrary, model);
+  }
+
+  protected void setupFactories(Map aFactoriesMap, FileMonitor aFileMonitor) throws MirLocalizerException, MirLocalizerFailure {
+    ProducerConfigReader reader;
+    ProducerNodeBuilderLibrary library = new ProducerNodeBuilderLibrary();
+    setupProducerNodeBuilderLibrary(library);
+    List usedFiles = new Vector();
+
+    aFileMonitor.clear();
+    reader = new ProducerConfigReader();
+    reader.parseFile(MirGlobal.getConfigProperty("Home") + "/" + MirGlobal.getConfigProperty("Mir.Localizer.ProducerConfigFile"), library, aFactoriesMap, usedFiles);
+
+    Iterator i = usedFiles.iterator();
+    while (i.hasNext())
+      aFileMonitor.addFile((File) i.next());
+
+    setupFactories(aFactoriesMap);
+  }
+
+  protected void setupFactories(Map aFactoriesMap ) throws MirLocalizerException, MirLocalizerFailure {
     CompositeProducerNode node;
 
-    node = new CompositeProducerNode();
-    setupContentFactory( node );
-    aFactoriesMap.put("content", new NodedProducerFactory(node));
-
-    node = new CompositeProducerNode();
-    setupStartPageFactory( node );
-    aFactoriesMap.put("startpage", new NodedProducerFactory(node));
-
-    node = new CompositeProducerNode();
-    setupSynchronizationFactory( node );
-    aFactoriesMap.put("synchronization", new NodedProducerFactory(node));
-
-    node = new CompositeProducerNode();
-    setupStaticFactory( node );
-    aFactoriesMap.put("static", new NodedProducerFactory(node));
-
-    node = new CompositeProducerNode();
-    setupTopicsFactory( node );
-    aFactoriesMap.put("topics", new NodedProducerFactory(node));
-
-    aFactoriesMap.put("media",
-      new CompositeProducerFactory( new ProducerFactory[] {
-        new OldProducerAdapterFactory(new ProducerImages()),
-        new OldProducerAdapterFactory(new ProducerAudio()),
-        new OldProducerAdapterFactory(new ProducerVideo()),
-        new OldProducerAdapterFactory(new ProducerOther())
-      } )
-    );
+    try {
+      node = new CompositeProducerNode();
+      setupContentFactory( node );
+      if (node.getNrSubNodes()>0)
+        aFactoriesMap.put("content", new NodedProducerFactory(node));
+
+      node = new CompositeProducerNode();
+      setupStartPageFactory( node );
+      if (node.getNrSubNodes()>0)
+        aFactoriesMap.put("startpage", new NodedProducerFactory(node));
+
+      node = new CompositeProducerNode();
+      setupSynchronizationFactory( node );
+      if (node.getNrSubNodes()>0)
+        aFactoriesMap.put("synchronization", new NodedProducerFactory(node));
+
+      node = new CompositeProducerNode();
+      setupStaticFactory( node );
+      if (node.getNrSubNodes()>0)
+        aFactoriesMap.put("static", new NodedProducerFactory(node));
+
+      node = new CompositeProducerNode();
+      setupTopicsFactory( node );
+      if (node.getNrSubNodes()>0)
+        aFactoriesMap.put("topics", new NodedProducerFactory(node));
+
+      aFactoriesMap.put("media",
+                   new CompositeProducerFactory( new ProducerFactory[] {
+                      new OldProducerAdapterFactory(new ProducerImages()),
+                      new OldProducerAdapterFactory(new ProducerAudio()),
+                      new OldProducerAdapterFactory(new ProducerVideo()),
+                      new OldProducerAdapterFactory(new ProducerOther())
+                  } )
+      );
+    }
+    catch (Exception e) {
+      throw new MirLocalizerFailure(e);
+    }
   };
 }
diff --git a/source/mircoders/localizer/basic/MirBasicWriterEngine.java b/source/mircoders/localizer/basic/MirBasicWriterEngine.java
new file mode 100755 (executable)
index 0000000..59f6f90
--- /dev/null
@@ -0,0 +1,47 @@
+package mircoders.localizer.basic;
+
+import java.util.*;
+import java.io.*;
+import mir.generator.*;
+import mircoders.localizer.*;
+
+public class MirBasicWriterEngine implements WriterEngine {
+  private String defaultEncoding;
+
+  public MirBasicWriterEngine(String aDefaultEncoding) {
+    defaultEncoding = aDefaultEncoding;
+  }
+
+  public Object openWriter(String anIdentifier, String anEncoding) throws MirLocalizerFailure {
+    String encoding;
+    File file;
+    File dir;
+
+    if (anEncoding!=null && !anEncoding.equals(""))
+      encoding = anEncoding;
+    else
+      encoding = defaultEncoding;
+//      MirGlobal.getConfigProperty("Mir.DefaultEncoding");
+
+    try {
+      file = new File( anIdentifier );
+      dir = new File(file.getParent());
+        if (dir!=null && !dir.exists()){
+          dir.mkdirs();
+      }
+
+      return new PrintWriter(
+        new OutputStreamWriter(
+          new FileOutputStream(file), encoding
+        )
+      );
+    }
+    catch (Throwable t) {
+      throw new MirLocalizerFailure("Failure while opening a PrintWriter: "+t.getMessage(),t);
+    }
+  };
+
+  public void closeWriter(Object aWriter) {
+    ((PrintWriter) aWriter).close();
+  };
+}
\ No newline at end of file
index 9ce0c0d..6bd3108 100755 (executable)
@@ -16,7 +16,7 @@ public class CompositeProducer implements mir.producer.Producer {
     producers.add(aProducer);
   }
 
-  public void produce( PrintWriter aLogger ) throws ProducerFailure {
+  public void produce( PrintWriter aLogger ) throws ProducerFailure, ProducerExc {
     Iterator i;
 
     i=producers.iterator();
index a060b1d..4019537 100755 (executable)
@@ -45,13 +45,13 @@ public class CompositeProducerFactory implements ProducerFactory {
     }
   }
 
-  public mir.producer.Producer makeProducer(String aVerb) throws ProducerFailure {
+  public mir.producer.Producer makeProducer(String aVerb, Map aBasicValueSet) throws ProducerExc, ProducerFailure {
     CompositeProducer result = new CompositeProducer();
 
     Iterator i=factoriesForVerb(aVerb).iterator();
 
     while (i.hasNext())
-      result.addProducer(((ProducerFactory) i.next()).makeProducer(aVerb));
+      result.addProducer(((ProducerFactory) i.next()).makeProducer(aVerb, aBasicValueSet));
 
     return result;
   }
diff --git a/source/mircoders/producer/GeneratingProducerNode.java b/source/mircoders/producer/GeneratingProducerNode.java
deleted file mode 100755 (executable)
index c19b90d..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-package mircoders.producer;
-
-import java.util.*;
-import java.io.*;
-import mir.util.*;
-import mir.producer.*;
-import mir.generator.*;
-import mircoders.global.*;
-import mircoders.localizer.*;
-
-public class GeneratingProducerNode implements ProducerNode {
-  private String generatorExpression;
-  private String destinationExpression;
-  private String encodingExpression;
-
-  public GeneratingProducerNode(String aGenerator, String aDestination, String anEncoding) {
-    generatorExpression=aGenerator;
-    destinationExpression=aDestination;
-    encodingExpression=anEncoding;
-  }
-  public GeneratingProducerNode(String aGenerator, String aDestination) {
-    this(aGenerator, aDestination, "");
-  }
-
-  public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure {
-    Generator generator;
-    PrintWriter printWriter;
-    String generatorIdentifier;
-    String destinationIdentifier;
-    String encodingIdentifier;
-
-    long startTime;
-    long endTime;
-
-    startTime = System.currentTimeMillis();
-    try {
-      destinationIdentifier = ParameterExpander.expandExpression( aValueMap, destinationExpression );
-      generatorIdentifier = ParameterExpander.expandExpression( aValueMap, generatorExpression );
-      encodingIdentifier = ParameterExpander.expandExpression( aValueMap, encodingExpression );
-
-      aLogger.println("Generating " + generatorIdentifier + " into " + destinationIdentifier + " using encoding " + encodingIdentifier);
-      aLogger.flush();
-      printWriter = MirGlobal.localizer().producerAssistant().openWriter( destinationIdentifier, encodingIdentifier);
-      generator = MirGlobal.localizer().generators().makeGenerator( generatorIdentifier );
-      generator.generate(printWriter, aValueMap, aLogger);
-      MirGlobal.localizer().producerAssistant().closeWriter( printWriter );
-    }
-    catch (Throwable t) {
-      aLogger.println("  error while generating: " + t.getMessage());
-      aLogger.flush();
-    }
-    endTime = System.currentTimeMillis();
-
-    aLogger.println("  Time: " + (endTime-startTime) + " ms<br>");
-    aLogger.flush();
-  }
-
-  public Set buildVerbSet() {
-    return new HashSet();
-  }
-}
\ No newline at end of file
index e3d4d59..2396fb1 100755 (executable)
@@ -8,7 +8,6 @@ import org.apache.lucene.index.*;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 
-
 import freemarker.template.*;
 
 
@@ -26,7 +25,7 @@ import mircoders.storage.*;
 public class IndexingProducerNode implements ProducerNode {
   private String contentKey;
   private String indexPath;
-    
+
 
   public IndexingProducerNode(String aContentKey, String pathToIndex) {
     contentKey = aContentKey;
@@ -42,82 +41,82 @@ public class IndexingProducerNode implements ProducerNode {
     long endTime;
 
     startTime = System.currentTimeMillis();
-    
+
     try {
       data = ParameterExpander.findValueForKey( aValueMap, contentKey );
 
       if (! (data instanceof EntityAdapter)) {
         throw new ProducerFailure("IndexingProducerNode: value of '"+contentKey+"' is not an EntityAdapter, but an " + data.getClass().getName(), null);
       }
-      
+
       entity = ((EntityAdapter) data).getEntity();
       if (! (entity instanceof EntityContent)) {
         throw new ProducerFailure("IndexingProducerNode: value of '"+contentKey+"' is not a content EntityAdapter, but a " + entity.getClass().getName() + " adapter", null);
       }
       aLogger.println("Indexing " + (String) entity.getValue("id") + " into " + indexPath);
       aLogger.flush();
-      
+
       IndexReader indexReader = IndexReader.open(indexPath);
       indexReader.delete(new Term("id",entity.getValue("id")));
       indexReader.close();
-      
+
       indexWriter = new IndexWriter(indexPath, new StandardAnalyzer(), false);
       Document theDoc =  new Document();
-      
+
       // Keyword is stored and indexed, but not tokenized
-      // Text is tokenized,stored, indexed 
+      // Text is tokenized,stored, indexed
       // Unindexed is not tokenized or indexed, only stored
       // Unstored is tokenized and indexed, but not stored
+
       theDoc.add(Field.Keyword("id",entity.getValue("id")));
       theDoc.add(Field.Keyword("where",entity.getValue("publish_path")+entity.getValue("id")+".shtml"));
       theDoc.add(Field.Text("creator",entity.getValue("creator")));
       theDoc.add(Field.Text("title",entity.getValue("title")));
       theDoc.add(Field.Keyword("webdb_create",entity.getValue("webdb_create_formatted")));
       theDoc.add(Field.UnStored("content_and_description",entity.getValue("description")+entity.getValue("content_data")));
-      
+
       //topics
       TemplateModel topics=entity.get("to_topics");
       aLogger.println("THE CLASS NAME WAS: "+entity.get("to_topics").getClass().getName());
       while (((TemplateListModel)topics).hasNext()){
-         theDoc.add(Field.UnStored("topic",((TemplateHashModel)((TemplateListModel)topics).next()).get("title").toString()));
+          theDoc.add(Field.UnStored("topic",((TemplateHashModel)((TemplateListModel)topics).next()).get("title").toString()));
       }
 
-      
+
       //media
-      
+
       //images
       TemplateModel images=entity.get("to_media_images");
       if (images != null){
-         theDoc.add(Field.UnStored("media","images"));
+          theDoc.add(Field.UnStored("media","images"));
       }
       //audio
       TemplateModel audio=entity.get("to_media_audio");
       if (audio != null){
-         theDoc.add(Field.UnStored("media","audio"));
+          theDoc.add(Field.UnStored("media","audio"));
       }
       //video
       TemplateModel video=entity.get("to_media_video");
       if (video != null){
-         theDoc.add(Field.UnStored("media","video"));
+          theDoc.add(Field.UnStored("media","video"));
       }
 
       //comments-just aggregate all relevant fields
       String commentsAggregate = "";
       TemplateModel comments=entity.get("to_comments");
       if (comments != null){
-       while (((TemplateListModel)comments).hasNext()){
-         TemplateModel aComment = ((TemplateListModel)comments).next();
-         commentsAggregate = commentsAggregate + " " + ((TemplateHashModel)aComment).get("title").toString() 
-           + " " + ((TemplateHashModel)aComment).get("creator").toString() 
-           + " " + ((TemplateHashModel)aComment).get("text").toString();
-       }
+        while (((TemplateListModel)comments).hasNext()){
+          TemplateModel aComment = ((TemplateListModel)comments).next();
+          commentsAggregate = commentsAggregate + " " + ((TemplateHashModel)aComment).get("title").toString()
+            + " " + ((TemplateHashModel)aComment).get("creator").toString()
+            + " " + ((TemplateHashModel)aComment).get("text").toString();
+        }
       }
       theDoc.add(Field.UnStored("comments",commentsAggregate));
 
       indexWriter.addDocument(theDoc);
       indexWriter.close();
-      
+
     }
     catch (Throwable t) {
       aLogger.println("Error while indexing content: " + t.getMessage());
@@ -125,12 +124,12 @@ public class IndexingProducerNode implements ProducerNode {
       //should remove index lock here.....jd
       throw new ProducerFailure(t.getMessage(), t);
     }
-      
-      
-  
-      
+
+
+
+
     endTime = System.currentTimeMillis();
-    
+
     aLogger.println("  IndexTime: " + (endTime-startTime) + " ms<br>");
     aLogger.flush();
   }
diff --git a/source/mircoders/producer/NodedProducerFactory.java b/source/mircoders/producer/NodedProducerFactory.java
deleted file mode 100755 (executable)
index 220eaa1..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package mircoders.producer;
-
-import java.util.*;
-import mir.producer.*;
-import mircoders.global.*;
-
-public class NodedProducerFactory implements ProducerFactory {
-  private ProducerNode rootNode;
-
-  public NodedProducerFactory(ProducerNode aRootNode) {
-    rootNode = aRootNode;
-  }
-
-  public mir.producer.Producer makeProducer(String aVerb) throws ProducerFailure {
-    Map baseValues;
-
-    try {
-      baseValues = new HashMap();
-
-      MirGlobal.localizer().producerAssistant().initializeGenerationValueSet(baseValues);
-
-      return new NodedProducer(rootNode, aVerb, baseValues);
-    }
-    catch (Throwable t) {
-      throw new ProducerFailure(t.getMessage(), t);
-    }
-  };
-
-  public Iterator verbs() {
-    Set verbSet = rootNode.buildVerbSet();
-
-    if (verbSet.isEmpty()) {
-      verbSet = new HashSet();
-
-      verbSet.add("(default)");
-    }
-
-    return verbSet.iterator();
-  };
-}
-
index ffb9cdb..4d5cea0 100755 (executable)
@@ -19,7 +19,7 @@ public class OldProducerAdapterFactory implements ProducerFactory {
     oldProducer = anOldProducer;
   }
 
-  public mir.producer.Producer makeProducer(String aVerb) {
+  public mir.producer.Producer makeProducer(String aVerb, Map anInitialValues) {
     return new OldProducerAdapter(oldProducer, new Boolean(aVerb.equals("all")));
   }
 }
\ No newline at end of file
index 1c0ed6b..4585604 100755 (executable)
@@ -4,13 +4,7 @@ import java.util.*;
 import java.io.*;
 import mir.util.*;
 import mir.producer.*;
-import mir.generator.*;
 import mir.misc.PDFUtil;
-import mircoders.global.*;
-import mircoders.localizer.*;
-
-
-
 
 public class PDFGeneratingProducerNode implements ProducerNode {
   private String generatorExpression;
@@ -22,27 +16,27 @@ public class PDFGeneratingProducerNode implements ProducerNode {
   }
 
   public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure {
-      
+
     String generatorIdentifier;
     String destinationIdentifier;
 
-         long startTime;
-         long endTime;
+          long startTime;
+          long endTime;
 
-         startTime = System.currentTimeMillis();
+          startTime = System.currentTimeMillis();
     try {
-      
+
       destinationIdentifier = ParameterExpander.expandExpression( aValueMap, destinationExpression );
       generatorIdentifier = ParameterExpander.expandExpression( aValueMap, generatorExpression );
 
       aLogger.println("Generating " + generatorIdentifier + " into " + destinationIdentifier);
       aLogger.flush();
-      
+
       PDFUtil.makePDF(generatorIdentifier,destinationIdentifier);
 
-       }
-       catch (Throwable t) {
-         aLogger.println("  error while generating: " + t.getMessage());
+        }
+        catch (Throwable t) {
+          aLogger.println("  error while generating: " + t.getMessage());
       aLogger.flush();
     }
     endTime = System.currentTimeMillis();
diff --git a/source/mircoders/producer/reader/SupplementalProducerNodeBuilders.java b/source/mircoders/producer/reader/SupplementalProducerNodeBuilders.java
new file mode 100755 (executable)
index 0000000..9c71ec8
--- /dev/null
@@ -0,0 +1,77 @@
+package mircoders.producer.reader;
+
+import java.util.*;
+import mir.producer.*;
+import mir.producer.reader.*;
+import mir.util.*;
+import mir.entity.adapter.*;
+import mir.generator.*;
+import mircoders.producer.*;
+
+public class SupplementalProducerNodeBuilders {
+
+  public static void registerBuilders(ProducerNodeBuilderLibrary aBuilderLibrary, EntityAdapterModel aModel) {
+    aBuilderLibrary.registerBuilder("ModifyContent", ContentModifyingProducerNodeBuilder.class);
+    aBuilderLibrary.registerBuilder("MarkContent", ContentMartkingProducerNodeBuilder.class);
+  }
+
+  public static class ContentMartkingProducerNodeBuilder extends DefaultProducerNodeBuilders.AbstractProducerNodeBuilder {
+    private final static String   MARKER_KEY_ATTRIBUTE = DefaultProducerNodeBuilders.KEY_ATTRIBUTE;
+    private final static String[] MARKER_REQUIRED_ATTRIBUTES = { MARKER_KEY_ATTRIBUTE };
+    private final static String[] MARKER_OPTIONAL_ATTRIBUTES = {};
+    private final static String[] MARKER_SUBNODES = {};
+
+    private String key;
+
+    public ContentMartkingProducerNodeBuilder() {
+      super(MARKER_SUBNODES);
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributes(anAttributes, MARKER_REQUIRED_ATTRIBUTES, MARKER_OPTIONAL_ATTRIBUTES);
+
+      key = (String) anAttributes.get(MARKER_KEY_ATTRIBUTE);
+    };
+
+    public ProducerNode constructNode() {
+      return new ContentMarkingProducerNode(key);
+    };
+  }
+
+
+  public static class ContentModifyingProducerNodeBuilder extends DefaultProducerNodeBuilders.AbstractProducerNodeBuilder {
+    private final static String   MODIFYER_KEY_ATTRIBUTE = DefaultProducerNodeBuilders.KEY_ATTRIBUTE;
+    private final static String   MODIFYER_FIELD_ATTRIBUTE = "field";
+    private final static String   MODIFYER_VALUE_ATTRIBUTE = "value";
+    private final static String[] MODIFYER_REQUIRED_ATTRIBUTES = { MODIFYER_KEY_ATTRIBUTE, MODIFYER_FIELD_ATTRIBUTE, MODIFYER_VALUE_ATTRIBUTE };
+    private final static String[] MODIFYER_OPTIONAL_ATTRIBUTES = {};
+    private final static String[] MODIFYER_SUBNODES = {};
+
+    private String key;
+    private String field;
+    private String value;
+
+    public ContentModifyingProducerNodeBuilder() {
+      super(MODIFYER_SUBNODES);
+    }
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      ReaderTool.checkAttributes(anAttributes, MODIFYER_REQUIRED_ATTRIBUTES, MODIFYER_OPTIONAL_ATTRIBUTES);
+
+      key = (String) anAttributes.get(MODIFYER_KEY_ATTRIBUTE);
+      field = (String) anAttributes.get(MODIFYER_FIELD_ATTRIBUTE);
+      value = (String) anAttributes.get(MODIFYER_VALUE_ATTRIBUTE);
+    };
+
+    public ProducerNode constructNode() {
+      return new ContentModifyingProducerNode(key, field, value);
+    };
+  }
+
+
+/*
+  TODO:
+        [ ] Media Producing
+*/
+
+}
\ No newline at end of file
index 4d275ce..82ac417 100755 (executable)
@@ -158,10 +158,17 @@ public class ServletModuleOpenIndy extends ServletModule
         }
         else {
           DatabaseContent.getInstance().setUnproduced("id="+aid);
+
+
+          try {
+            EntityComment comment = (EntityComment) DatabaseComment.getInstance().selectById(id);
+            MirGlobal.localizer().openPostings().afterCommentPosting(comment);
+          }
+          catch (Throwable t) {
+            throw new ServletModuleException(t.getMessage());
+          }
           
-          EntityComment comment = (EntityComment) DatabaseComment.getInstance().selectById(id);
           
-          MirGlobal.localizer().openPostings().afterCommentPosting(comment);
           
         }
 
@@ -503,13 +510,17 @@ public class ServletModuleOpenIndy extends ServletModule
       contentEnt.setValueForProperty("is_published","1");
       contentEnt.update();
 
-      MirGlobal.localizer().openPostings().afterContentPosting(contentEnt);
 
 
       //dereference mp. -mh
       mp=null;
 
-
+      try {
+        MirGlobal.localizer().openPostings().afterContentPosting(contentEnt);
+      }
+      catch (Throwable t) {
+        throw new ServletModuleException(t.getMessage());
+      }
     }
     catch (IOException e) { throw new ServletModuleException("IOException: "+ e.toString());}
     catch (StorageObjectException e) { throw new ServletModuleException("StorageObjectException" + e.toString());}
index aeca249..02f0331 100755 (executable)
@@ -6,7 +6,7 @@ import mircoders.localizer.basic.*;
 
 public class BoliviaLocalizer extends MirBasicLocalizer {
 
-  public MirProducerLocalizer producers() {
+  public MirProducerLocalizer producers() throws MirLocalizerException {
     return new BoliviaProducerLocalizer();
   }
 
index 78d4c40..6214140 100755 (executable)
@@ -12,9 +12,10 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
 
   protected void setupContentFactory(CompositeProducerNode aProducerNode) {
     EntityEnumeratingProducerNode contentNode = null;
-    aProducerNode.clear();
 
     try {
+      aProducerNode.clear();
+
       contentNode =
           new EntityEnumeratingProducerNode( "content", model, "content",
             new CompositeProducerNode( new ProducerNode[] {
@@ -22,38 +23,42 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
                 new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                     new CompositeProducerNode( new ProducerNode[] {
                       new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                           "/producer/bolivia.indymedia.org/article.template",
                           "${config.storageRoot}/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.shtml"
                       ),
-                     new GeneratingProducerNode(
+                               new GeneratingProducerNode(
+                          MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                          MirGlobal.localizer().generators().makeWriterEngine(),\r
                           "/producer/bolivia.indymedia.org/printablecontent.template",
                           "${config.storageRoot}/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.fo"
                       ),
-                     new PDFGeneratingProducerNode(
-                         "${config.storageRoot}/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.fo",
-                         "${config.storageRoot}/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.pdf"
-                      ),
-                      new ContentMarkingProducerNode( "content" )
-                    } )
+                               new PDFGeneratingProducerNode(
+                                           "${config.storageRoot}/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.fo",
+                                       "${config.storageRoot}/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.pdf"
+                          ),
+                          new ContentMarkingProducerNode( "content" )
+                     } )
                 )
               ),
               new ContentMarkingProducerNode( "content")
             } )
           );
+      contentNode.addVerb( "new", "is_published='1' and is_produced='f'", "" );
+      contentNode.addVerb( "all", "is_published='1'", "" );
+  
+      aProducerNode.addSubNode( contentNode );
     }
     catch (Throwable t) {
       logger.printError("BoliviaProducerLocalizer.setupContentFactory: Exception "+t.getMessage());
     }
 
-    contentNode.addVerb( "new", "is_published='1' and is_produced='f'", "" );
-    contentNode.addVerb( "all", "is_published='1'", "" );
-
-    aProducerNode.addSubNode( contentNode );
   }
 
   protected void setupStartPageFactory(CompositeProducerNode aProducerNode) {
-    aProducerNode.clear();
     try {
+      aProducerNode.clear();
       aProducerNode.addSubNode(
             new EntityListProducerNode("newswire", model, "content",
                     "is_published='1' and to_article_type = ${articletype.newswire}", "date desc, webdb_create desc", 40, 0,
@@ -68,6 +73,8 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
                       new EntityEnumeratingProducerNode( "language", model, "language", "", "",
                         new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                           new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                               "/producer/bolivia.indymedia.org/startpage.template",
                               "${config.storageRoot}/${language.code}/index.shtml"
                           )
@@ -84,6 +91,8 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
               "is_published='1' and to_article_type = ${articletype.feature}",
               "webdb_create desc, date desc", 10, 0,
           new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
               "/producer/bolivia.indymedia.org/featuressyndication.template",
               "${config.storageRoot}/features.1-0.rdf",
               "UTF-8"
@@ -97,15 +106,17 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
   }
 
   protected void setupStaticFactory(CompositeProducerNode aProducerNode) {
-    aProducerNode.clear();
 
     try {
+      aProducerNode.clear();
       aProducerNode.addSubNode(
           new EntityListProducerNode("languages", model, "language",
                   "", "code", 10, 0,
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/bolivia.indymedia.org/navigation.template",
                     "${config.storageRoot}/${language.code}/navigation.inc"
                 )
@@ -135,6 +146,8 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/bolivia.indymedia.org/topic.template",
                     "${config.storageRoot}/${language.code}/${topic.filename}/${topic.filename}${batch.current.identifier}.shtml"
                 )
@@ -146,6 +159,8 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/bolivia.indymedia.org/topicnavigation.template",
                     "${config.storageRoot}/${language.code}/${topic.filename}/${topic.filename}navigation.inc"
                 )
@@ -178,6 +193,8 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
                       new EntityEnumeratingProducerNode( "language", model, "language", "", "",
                         new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                           new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                               "/producer/bolivia.indymedia.org/topicstartpage.template",
                               "${config.storageRoot}/${language.code}/${topic.filename}/index.shtml"
                           )
@@ -212,6 +229,8 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/bolivia.indymedia.org/newswire.template",
                     "${config.storageRoot}/${language.code}/newswire/newswire${batch.current.identifier}.shtml"
                 )
@@ -223,6 +242,8 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/bolivia.indymedia.org/newswirenavigation.template",
                     "${config.storageRoot}/${language.code}/newswire/newswirenavigation.inc"
                 )
@@ -244,6 +265,8 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/bolivia.indymedia.org/feature.template",
                     "${config.storageRoot}/${language.code}/feature/feature${batch.current.identifier}.shtml"
                 )
@@ -255,6 +278,8 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/bolivia.indymedia.org/featurenavigation.template",
                     "${config.storageRoot}/${language.code}/feature/featurenavigation.inc"
                 )
@@ -272,4 +297,6 @@ public class BoliviaProducerLocalizer extends MirBasicProducerLocalizer {
       logger.printError("BoliviaProducerLocalizer.setupFactories: Exception "+t.getMessage());
     }
   }
+  
+  
 }
index 072b6b4..9125eda 100755 (executable)
@@ -6,7 +6,7 @@ import mircoders.localizer.basic.*;
 
 public class EcuadorLocalizer extends MirBasicLocalizer {
 
-  public MirProducerLocalizer producers() {
+  public MirProducerLocalizer producers() throws MirLocalizerException {
     return new EcuadorProducerLocalizer();
   }
 
index 6e34427..b0ae278 100755 (executable)
@@ -22,6 +22,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
                 new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                     new CompositeProducerNode( new ProducerNode[] {
                       new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                           "/producer/ecuador.indymedia.org/article.template",
                           "${config.storageRoot}/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.shtml"
                       ),
@@ -61,6 +63,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
                       new EntityEnumeratingProducerNode( "language", model, "language", "", "",
                         new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                           new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                               "/producer/ecuador.indymedia.org/startpage.template",
                               "${config.storageRoot}/${language.code}/index.shtml"
                           )
@@ -88,6 +92,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/navigation.template",
                     "${config.storageRoot}/${language.code}/navigation.inc"
                 )
@@ -116,6 +122,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/topic.template",
                     "${config.storageRoot}/${language.code}/${topic.filename}/${topic.filename}${batch.current.identifier}.shtml"
                 )
@@ -127,6 +135,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/topicnavigation.template",
                     "${config.storageRoot}/${language.code}/${topic.filename}/${topic.filename}navigation.inc"
                 )
@@ -168,6 +178,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/newswire.template",
                     "${config.storageRoot}/${language.code}/newswire/newswire${batch.current.identifier}.shtml"
                 )
@@ -179,6 +191,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/newswirenavigation.template",
                     "${config.storageRoot}/${language.code}/newswire/newswirenavigation.inc"
                 )
@@ -200,6 +214,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/feature.template",
                     "${config.storageRoot}/${language.code}/feature/feature${batch.current.identifier}.shtml"
                 )
@@ -211,6 +227,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/featurenavigation.template",
                     "${config.storageRoot}/${language.code}/feature/featurenavigation.inc"
                 )
@@ -235,6 +253,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/media.template",
                     "${config.storageRoot}/${language.code}/media/images${batch.current.identifier}.shtml"
                 )
@@ -246,6 +266,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/medianavigation.template",
                     "${config.storageRoot}/${language.code}/media/imagesnavigation.inc"
                 )
@@ -257,8 +279,10 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
       archiveNode.addVerb("all", -1);
 
       aFactoriesMap.put( "imagearchive", new NodedProducerFactory( 
-        new EvaluatedAssignmentProducerNode( "mediatype", "images",
-          archiveNode ) ) );
+          new CompositeProducerNode( new ProducerNode[] {
+            new ExpandedAssignmentProducerNode( "mediatype", "images" ),
+            archiveNode 
+          } ) ) );
 
       archiveNode =
         new EntityBatchingProducerNode( "articles", "batch", model, "content",
@@ -271,6 +295,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/media.template",
                     "${config.storageRoot}/${language.code}/media/audio${batch.current.identifier}.shtml"
                 )
@@ -282,6 +308,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/medianavigation.template",
                     "${config.storageRoot}/${language.code}/media/audionavigation.inc"
                 )
@@ -293,8 +321,9 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
       archiveNode.addVerb("all", -1);
 
       aFactoriesMap.put( "audioarchive", new NodedProducerFactory( 
-        new EvaluatedAssignmentProducerNode( "mediatype", "audio",
-          archiveNode ) ) );
+          new CompositeProducerNode( new ProducerNode[] {
+            new ExpandedAssignmentProducerNode( "mediatype", "audio" ),
+            archiveNode } ) ) );
 
       archiveNode =
         new EntityBatchingProducerNode( "articles", "batch", model, "content",
@@ -307,6 +336,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/media.template",
                     "${config.storageRoot}/${language.code}/media/video${batch.current.identifier}.shtml"
                 )
@@ -318,6 +349,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/medianavigation.template",
                     "${config.storageRoot}/${language.code}/media/videonavigation.inc"
                 )
@@ -329,8 +362,9 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
       archiveNode.addVerb("all", -1);
 
       aFactoriesMap.put( "videoarchive", new NodedProducerFactory( 
-        new EvaluatedAssignmentProducerNode( "mediatype", "video",
-          archiveNode ) ) );
+          new CompositeProducerNode( new ProducerNode[] {
+            new ExpandedAssignmentProducerNode( "mediatype", "video") ,
+          archiveNode } ) ) );
 
       archiveNode =
         new EntityBatchingProducerNode( "articles", "batch", model, "content",
@@ -343,6 +377,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/media.template",
                     "${config.storageRoot}/${language.code}/media/other${batch.current.identifier}.shtml"
                 )
@@ -354,6 +390,8 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/ecuador.indymedia.org/medianavigation.template",
                     "${config.storageRoot}/${language.code}/media/othernavigation.inc"
                 )
@@ -365,8 +403,9 @@ public class EcuadorProducerLocalizer extends MirBasicProducerLocalizer {
       archiveNode.addVerb("all", -1);
 
       aFactoriesMap.put( "otherarchive", new NodedProducerFactory( 
-        new EvaluatedAssignmentProducerNode( "mediatype", "other",
-          archiveNode ) ) );
+          new CompositeProducerNode( new ProducerNode[] {
+            new ExpandedAssignmentProducerNode( "mediatype", "other" ),
+          archiveNode } ) ) );
     }
     catch (Throwable t) {
       logger.printError("EcuadorProducerLocalizer.setupFactories: Exception "+t.getMessage());
index e19d8c1..3d6f791 100755 (executable)
@@ -6,7 +6,7 @@ import mircoders.localizer.basic.*;
 
 public class EHLocalizer extends MirBasicLocalizer {
 
-  public MirProducerLocalizer producers() {
+  public MirProducerLocalizer producers() throws MirLocalizerException {
     return new EHProducerLocalizer();
   }
 
index f4e53e8..d89c2f7 100755 (executable)
@@ -23,6 +23,8 @@ public class EHProducerLocalizer extends MirBasicProducerLocalizer {
                 new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                     new CompositeProducerNode( new ProducerNode[] {
                       new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                           "/producer/euskalherria.indymedia.org/article.template",
                           "${config.storageRoot}/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.shtml"
                       ),
@@ -65,6 +67,8 @@ public class EHProducerLocalizer extends MirBasicProducerLocalizer {
                         new EntityListProducerNode("breaking", model, "breakingNews",
                                   "", "webdb_create desc", 5, 0,
                           new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                               "/producer/euskalherria.indymedia.org/startpage.template",
                               "${config.storageRoot}/${language.code}/index.shtml"
                           )
@@ -92,6 +96,8 @@ public class EHProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/euskalherria.indymedia.org/navigation.template",
                     "${config.storageRoot}/${language.code}/navigation.inc"
                 )
@@ -119,6 +125,8 @@ public class EHProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/euskalherria.indymedia.org/topic.template",
                     "${config.storageRoot}/${language.code}/${topic.filename}/${topic.filename}${batch.current.identifier}.shtml"
                 )
@@ -130,6 +138,8 @@ public class EHProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/euskalherria.indymedia.org/topicnavigation.template",
                     "${config.storageRoot}/${language.code}/${topic.filename}/${topic.filename}navigation.inc"
                 )
@@ -157,8 +167,4 @@ public class EHProducerLocalizer extends MirBasicProducerLocalizer {
     }
 
   }
-
-  protected void setupFactories(Map aFactoriesMap ) {
-    super.setupFactories(aFactoriesMap);
-  }
 }
index c3c9f6f..fb8dcad 100755 (executable)
@@ -6,7 +6,7 @@ import mircoders.localizer.basic.*;
 
 public class IndyNLLocalizer extends MirBasicLocalizer {
 
-  public MirProducerLocalizer producers() {
+  public MirProducerLocalizer producers() throws MirLocalizerException {
     return new IndyNLProducerLocalizer();
   }
 
index 597f2cc..39de45f 100755 (executable)
@@ -20,6 +20,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/indymedia.nl/trashedcomments.template",
                     "${config.storageRoot}/trashbin/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.trashed.shtml"
                 )
@@ -38,10 +40,14 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
           new EntityEnumeratingProducerNode( "content", model, "content",
             new CompositeProducerNode( new ProducerNode[] {
               new GeneratingProducerNode(
+                  MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                  MirGlobal.localizer().generators().makeWriterEngine(),\r
                   "/producer/indymedia.nl/content.template",
                   "${config.storageRoot}/content/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.inc"
               ),
               new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                   "/producer/indymedia.nl/comments.template",
                   "${config.storageRoot}/content/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.comments.inc"
               ),
@@ -49,18 +55,20 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
                 new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                     new CompositeProducerNode( new ProducerNode[] {
                       new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                           "/producer/indymedia.nl/article.template",
                           "${config.storageRoot}/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.shtml"
                       ),
                       new FileDateSettingProducerNode(
                           "${config.storageRoot}/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.shtml",
-                          "content.date.date"
+                          "content.creationdate.date"
                       )
                     } )
                 )
               ),
               new ContentMarkingProducerNode( "content" ),
-              new ScriptCallingProducerNode("/home/www-data/bin/addarticle \"http://prod.indymedia.nl/nl/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.shtml\"")
+              new ScriptCallingProducerNode("/home/www-data/bin/addarticle http://prod.indymedia.nl/nl/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.shtml")
             } )
           );
 
@@ -76,10 +84,14 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
           new EntityEnumeratingProducerNode( "content", model, "content",
             new CompositeProducerNode( new ProducerNode[] {
               new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                   "/producer/indymedia.nl/removed.template",
                   "${config.storageRoot}/content/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.inc"
               ),
               new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                   "/producer/indymedia.nl/removed.template",
                   "${config.storageRoot}/content/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.comments.inc"
               ),
@@ -87,6 +99,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
                 new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                     new CompositeProducerNode( new ProducerNode[] {
                       new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                           "/producer/indymedia.nl/trashed.template",
                           "${config.storageRoot}/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.shtml"
                       ),
@@ -111,12 +125,16 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
           new EntityEnumeratingProducerNode( "content", model, "content",
             new CompositeProducerNode( new ProducerNode[] {
               new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                   "/producer/indymedia.nl/removed.template",
                   "${config.storageRoot}/content/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.inc"
               ),
               new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
                 new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                   new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                       "/producer/indymedia.nl/removed.template",
                       "${config.storageRoot}/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.shtml"
                   )
@@ -126,6 +144,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
                 new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                     new CompositeProducerNode( new ProducerNode[] {
                       new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                           "/producer/indymedia.nl/trashed.template",
                           "${config.storageRoot}/trashbin/${language.code}/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.shtml"
                       ),
@@ -161,6 +181,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
                         new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
                           new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                               new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                                   "/producer/indymedia.nl/newswire.template",
                                   "${config.storageRoot}/${language.code}/newswire.inc"
                               )
@@ -176,6 +198,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
                                 new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
                                   new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                                     new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                                         "/producer/indymedia.nl/features.template",
                                         "${config.storageRoot}/${language.code}/features.inc"
                                     )
@@ -183,18 +207,20 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
                                 ),
                                 new EntityEnumeratingProducerNode( "language", model, "language", "code='nl'", "",
                                   new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
-                                    new AssignmentProducerNode( "staticinclude", "1",
-                                      new EvaluatedAssignmentProducerNode( "topinclude", "/producer/indymedia.nl/top.template",
-                                      new EvaluatedAssignmentProducerNode( "bottominclude", "/producer/indymedia.nl/bottom.template",
-                                      new EvaluatedAssignmentProducerNode( "navinclude", "/producer/indymedia.nl/nav.template",
-                                      new EvaluatedAssignmentProducerNode( "newswireinclude", "/producer/indymedia.nl/newswire.template",
-                                      new EvaluatedAssignmentProducerNode( "featuresinclude", "/producer/indymedia.nl/features.template",
-                                        new GeneratingProducerNode(
-                                            "/producer/indymedia.nl/start.template",
-                                            "${config.storageRoot}/index.html"
-                                        )
-                                      )))))
-                                    )
+                                    new CompositeProducerNode( new ProducerNode[] {
+                                      new ExpandedAssignmentProducerNode( "staticinclude", "1"),
+                                      new ExpandedAssignmentProducerNode( "topinclude", "/producer/indymedia.nl/top.template"),
+                                      new ExpandedAssignmentProducerNode( "bottominclude", "/producer/indymedia.nl/bottom.template"),
+                                      new ExpandedAssignmentProducerNode( "navinclude", "/producer/indymedia.nl/nav.template"),
+                                      new ExpandedAssignmentProducerNode( "newswireinclude", "/producer/indymedia.nl/newswire.template"),
+                                      new ExpandedAssignmentProducerNode( "featuresinclude", "/producer/indymedia.nl/features.template"),
+                                      new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
+                                          "/producer/indymedia.nl/start.template",
+                                          "${config.storageRoot}/index.html"
+                                      )
+                                    } )
                                   )
                                 )
                               } )
@@ -222,6 +248,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/indymedia.nl/topic.template",
                     "${config.storageRoot}/${language.code}/${topic.filename}/${topic.filename}${batch.current.identifier}.shtml"
                 )
@@ -232,6 +260,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/indymedia.nl/topicnavigation.template",
                     "${config.storageRoot}/${language.code}/${topic.filename}/${topic.filename}navigation.inc"
                 )
@@ -265,48 +295,67 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
                 new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                   new CompositeProducerNode( new ProducerNode[] {
                     new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                         "/producer/indymedia.nl/nav.template",
                         "${config.storageRoot}/${language.code}/nav.inc"
                     ),
                     new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                         "/producer/indymedia.nl/top.template",
                         "${config.storageRoot}/${language.code}/top.inc"
                     ),
                     new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                         "/producer/indymedia.nl/bottom.template",
                         "${config.storageRoot}/${language.code}/bottom.inc"
                     ),
                     new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                         "/producer/indymedia.nl/style.template",
                         "${config.storageRoot}/${language.code}/style.css"
                     ),
                     new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                         "/producer/indymedia.nl/style2.template",
                         "${config.storageRoot}/${language.code}/style2.css"
                     ),
                     new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                         "/producer/indymedia.nl/trashtop.template",
                         "${config.storageRoot}/${language.code}/trashtop.inc"
                     ),
                     new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                         "/producer/indymedia.nl/articlepre.template",
                         "${config.storageRoot}/${language.code}/articlepre.shtml"
                     ),
                     new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                         "/producer/indymedia.nl/articlepost.template",
                         "${config.storageRoot}/${language.code}/articlepost.shtml"
                     ),
                     new AssignmentProducerNode( "staticinclude", "0",
-                      new EvaluatedAssignmentProducerNode( "topinclude", "/${language.code}/top.inc",
-                      new EvaluatedAssignmentProducerNode( "bottominclude", "/${language.code}/bottom.inc",
-                      new EvaluatedAssignmentProducerNode( "navinclude", "/${language.code}/nav.inc",
-                      new EvaluatedAssignmentProducerNode( "newswireinclude", "/${language.code}/newswire.inc",
-                      new EvaluatedAssignmentProducerNode( "featuresinclude", "/${language.code}/features.inc",
+                      new CompositeProducerNode( new ProducerNode[] {
+                        new ExpandedAssignmentProducerNode( "topinclude", "/${language.code}/top.inc" ),
+                        new ExpandedAssignmentProducerNode( "bottominclude", "/${language.code}/bottom.inc" ),
+                        new ExpandedAssignmentProducerNode( "navinclude", "/${language.code}/nav.inc" ),
+                        new ExpandedAssignmentProducerNode( "newswireinclude", "/${language.code}/newswire.inc" ),
+                        new ExpandedAssignmentProducerNode( "featuresinclude", "/${language.code}/features.inc" ),
                         new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                             "/producer/indymedia.nl/start.template",
                             "${config.storageRoot}/${language.code}/start.shtml"
                         )
-                      )))))
+                      })
                     )
 /*                    ,
                     new AssignmentProducerNode( "staticinclude", "0",
@@ -333,7 +382,7 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
     }
   }
 
-  protected void setupFactories(Map aFactoriesMap ) {
+  protected void setupFactories(Map aFactoriesMap ) throws MirLocalizerException {
     super.setupFactories(aFactoriesMap);
 
     ProducerNode node = null;
@@ -437,6 +486,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
               new EntityEnumeratingProducerNode( "content", model, "content",
                 new CompositeProducerNode( new ProducerNode[] {
                   new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                       "/producer/indymedia.nl/static.template",
                       "${config.storageRoot}/${language.code}/static/${content.edittitle}.shtml"
                   ),
@@ -468,6 +519,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
               new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
                 new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                   new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                       "/producer/indymedia.nl/trashbin.template",
                       "${config.storageRoot}/trashbin/${language.code}/trashbin${batch.current.identifier}.shtml"
                   )
@@ -478,6 +531,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
               new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
                 new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                   new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                       "/producer/indymedia.nl/trashbinnavigation.template",
                       "${config.storageRoot}/trashbin/${language.code}/trashbinnavigation.inc"
                   )
@@ -504,6 +559,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/indymedia.nl/feature.archive.template",
                     "${config.storageRoot}/${language.code}/feature/feature${batch.current.identifier}.shtml"
                 )
@@ -514,6 +571,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/indymedia.nl/feature.archive.navigation.template",
                     "${config.storageRoot}/${language.code}/feature/featurenavigation.inc"
                 )
@@ -538,6 +597,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/indymedia.nl/newswire.archive.template",
                     "${config.storageRoot}/${language.code}/newswire/newswire${batch.current.identifier}.shtml"
                 )
@@ -548,6 +609,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/indymedia.nl/newswire.archive.navigation.template",
                     "${config.storageRoot}/${language.code}/newswire/newswirenavigation.inc"
                 )
@@ -572,6 +635,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/indymedia.nl/openposting.archive.template",
                     "${config.storageRoot}/${language.code}/openposting/openposting${batch.current.identifier}.shtml"
                 )
@@ -582,6 +647,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/indymedia.nl/openposting.archive.navigation.template",
                     "${config.storageRoot}/${language.code}/openposting/openpostingnavigation.inc"
                 )
@@ -607,6 +674,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/indymedia.nl/commentedarticles.template",
                     "${config.storageRoot}/${language.code}/commentedarticles.shtml"
                 )
@@ -628,6 +697,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "code in ('nl', 'en')", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/indymedia.nl/opentrashbin.template",
                     "${config.storageRoot}/${language.code}/opentrashbin.shtml"
                 )
@@ -650,6 +721,8 @@ public class IndyNLProducerLocalizer extends MirBasicProducerLocalizer {
             new EntityEnumeratingProducerNode( "language", model, "language", "code in ('en')", "",
               new ResourceBundleProducerNode("lang", "bundles.producer_${language.code}",
                 new GeneratingProducerNode(
+                    MirGlobal.localizer().generators().makeGeneratorLibrary(),\r
+                    MirGlobal.localizer().generators().makeWriterEngine(),\r
                     "/producer/indymedia.nl/featuressyndication.template",
                     "${config.storageRoot}/features.1-0.rdf",
                     "UTF-8"
index ca3ac7b..f3f79ec 100755 (executable)
@@ -71,7 +71,7 @@
        <font size="-2">${lang("open.posting.topic.info")}</font>
         </td>
        <td colspan="2">
-       <select name="data.to_topic" size="3" multiple>
+       <select name="to_topic" size="3" multiple>
        <list extra.themenPopupData as t>
        <option value="${t.key}" <list to_topic as to><if (t.key == to)>selected</if></list>>${t.value}</option>
        </list>