Package de.maramuse.soundcomp.parser

Source Code of de.maramuse.soundcomp.parser.Process$DebugProxy

/*
* Copyright 2010 Jan Schmidt-Reinisch
*
* SoundComp - a sound processing library
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; in version 2.1
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

package de.maramuse.soundcomp.parser;

import java.text.MessageFormat;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import de.maramuse.soundcomp.math.Variable;
import de.maramuse.soundcomp.math.ident;
import de.maramuse.soundcomp.parser.SCParser.ParserVal;
import de.maramuse.soundcomp.parser.math.FormulaElement;
import de.maramuse.soundcomp.parser.math.FormulaElement1;
import de.maramuse.soundcomp.parser.math.FormulaElement2;
import de.maramuse.soundcomp.parser.math.FormulaElement3;
import de.maramuse.soundcomp.process.ConstStream;
import de.maramuse.soundcomp.process.NamedSource;
import de.maramuse.soundcomp.process.ProcessElement;
import de.maramuse.soundcomp.process.SampleAdvancer;
import de.maramuse.soundcomp.process.SourceStore;
import de.maramuse.soundcomp.process.StandardParameters;
import de.maramuse.soundcomp.process.StandardParameters.Parameter;
import de.maramuse.soundcomp.process.Stateful;
import de.maramuse.soundcomp.process.TypeMismatchException;
import de.maramuse.soundcomp.process.UnknownConnectionException;
import de.maramuse.soundcomp.process.ValueType;
import de.maramuse.soundcomp.util.NativeObjects;
import de.maramuse.soundcomp.util.ReadOnlyMap;
import de.maramuse.soundcomp.util.ReadOnlyMapImpl;

/**
* this symbol class represents a process definition it is special in that it represents a ProcessElement by itself (to
* simplify creation of the ProcessElement which otherwise would require massive duplication) This special treatment
* means that we will need extra care for memory allocations. The instances that represent real calculated processes
* will have a native object created for, that must be freed in the end. The instances that only live as parse tree
* objects would not need this. They have it anyway, so it must be deleted at some point. (TODO)
*
* A process has a substructure of:
*
*  - inputs that serve as sources of data
*  - inner process elements (which may be processes themselves, but that is irrelevant at this level) which serve as sources of as well as sinks for data.
*  - variables (which are just intermediates which serve to avoid duplicate calculation when the same term is needed multiple times).
*    variables, like process elements, are sources and sinks in one.
*  - outputs that serve as sinks for data
*
* When creating a process from parse data, or cloning it, all inner connections must be created.
*/
public class Process extends ParserVal implements ProcessElement, Stateful, ParserContext {

  // start of members for the parser part. each is a list of same type objects

  // the name of this process
  ParserVal name;

  // a list of input definitions for this process. currently just a name, later on also an input type
  ParserVal inputs;

  // a list of output definitions for this process. a name, and a formula that calculates the value
  ParserVal outputs;

  // a list of subprocesses. each element either is a process definition, or a process reference
  // that refers to a process that must be defined elsewhere. the former is more an uncommon abbreviation,
  // the latter should be the more common case. Each referred process must be defined or forward declared
  // prior to referral use, as on referral, connection data must already be available.
  //
  // Initial implementation will only allow process references here, not real subprocess definitions.
  // This is more a convenience issue - everything that the internal subprocesses allow can also be achieved
  // by referring to external processes.
  ParserVal subprocesses;

  // a list of variables. similar to outputs, but visible to the interior instead of to the exterior.
  // a variable is a kind of stateless subprocess definition
  ParserVal variables=null;

  // end of members for the parser part. each is a list of same type objects

  // start of members for the control logic and signal handling part.

  // a process template. unlike simpler process elements, this is not taken as a reproduction template for
  // createTemplate() due to the increased complexity. instead, each instance is recreated from scratch.
  // the initial template will still be calculated first as this is an ideal point for consistency checks.
  // all "copied instances" will not produce their own template, they will refer to the original, should they
  // ever need it. this member is probably redundant, but since we have to build the template anyway, why not
  // make it available. one never knows...
  ProcessElement template=null;

  // this map of subordinated data sources helps in consistency checks (duplicate name checks).
  ReadOnlyMapImpl<String, NamedSource> subs=null;

  // a map of subordinated process definitions.
  ReadOnlyMapImpl<String, Process> subprocessMap=new ReadOnlyMapImpl<String, Process>();
  ReadOnlyMapImpl<String, ProcessRef> subrefMap=new ReadOnlyMapImpl<String, ProcessRef>();

  // the sub-processes of this process do not register themselves with the AdvancerRegistry.
  // this process itself has to take care of advancing its subs. this may ease life cycle management
  // for the subs (they don't have to be known externally, their existence is hidden from the outside).
  Set<SampleAdvancer> advancableSubs=new TreeSet<SampleAdvancer>();
 
 
 
  // members for running the ProcessElement this process represents
  private long nativeSpace;
  private String instanceName;
  private String abstractName;
  private ReadOnlyMapImpl<Integer, ValueType> sourceTypes=new ReadOnlyMapImpl<Integer, ValueType>();
  private ReadOnlyMapImpl<Integer, ValueType> destinationTypes=new ReadOnlyMapImpl<Integer, ValueType>();
  private ReadOnlyMapImpl<Integer, SourceStore> sourceMap=new ReadOnlyMapImpl<Integer, SourceStore>();
  private ReadOnlyMapImpl<String, Parameter> namedInputs=new ReadOnlyMapImpl<String, Parameter>();
  private ReadOnlyMapImpl<String, Parameter> namedOutputs=new ReadOnlyMapImpl<String, Parameter>();
  private ReadOnlyMapImpl<Integer, SourceStore> destinationMap=new ReadOnlyMapImpl<Integer, SourceStore>();

  // members for the control logic
  private Map<String, ProcessElement> containedElements=new TreeMap<String, ProcessElement>();
  private Map<String, Variable> containedVariables=new TreeMap<String, Variable>();
  private static long copyCount=0;

  /**
   * The constructor that is used when the Parser builds the initial version of this Process(-Element) as a template for
   * the voice generation
   *
   * @param s
   *          the name of the element
   */
  Process(String s) {
  super(SCParser.PROCESS, s);
  NativeObjects.registerNativeObject(this);
  }

  /**
   * The constructor that is used when the Parser builds instances of derived classes (e.g. global processes that have
   * special properties). Note that the native object then should be handled in the derived constructors.
   *
   * @param s
   *          the name of the element
   */
  Process(int type, String s) {
  super(type, s);
  }

  /**
   * The constructor that is used when such a Process is created during runtime
   */
  Process() {
  super(SCParser.PROCESS, "~process");
  NativeObjects.registerNativeObject(this);
  }

  /**
   * The constructor that would be called if such an object is to be created from C++. This is not expected to happen
   * since in C++ the necessary information for filling in a custom process is not available. Nevertheless, this
   * constructor should be available so an accidental attempt to do so does not trigger a C++ Exception for the missing
   * constructor.
   *
   * @param ignoreme
   */
  Process(boolean ignoreme) {
  super(SCParser.ILLEGAL, "unexpected c++ created process");
  }

  /**
   * Set the ProcessElement name
   *
   * @param name
   */
  void setName(ParserVal name) {
  this.name=name;
  }

  /**
   * Set the inputs (Parser oriented)
   *
   * @param inputs
   */
  void setInputs(ParserVal inputs) {
  this.inputs=inputs;
  }

  /**
   * Set the variables (Parser oriented)
   *
   * @param variables
   */
  void setVariables(ParserVal variables) {
  this.variables=variables;
  }

  /**
   * Set the outputs (Parser oriented)
   *
   * @param outputs
   */
  void setOutputs(ParserVal outputs) {
  this.outputs=outputs;
  }

  void setTemplate(ProcessElement template) {
  this.template=template;
  }

  /**
   * Create a ProcessElement representing the Process data generated by the parser.
   *
   * @return a ProcessElement representing the Process data generated by the parser.
   * @throws UnknownConnectionException
   *           if an outer source is not found
   * @throws TypeMismatchException
   *           if an outer source provides unsuitable data
   */
  ProcessElement createTemplate() throws UnknownConnectionException, TypeMismatchException {

  /*
   * Master plan: first, gather all necessary information from the parse tree that is represented by appended objects.
   * a process may contain of: - subprocesses - variables - inputs - outputs
   *
   * All of these should be kept in treemaps by name.
   *
   * Inputs are implicit UnnamedSources that nothing has to be done for within the Process. The same applies for
   * variable readings. so they should be kept in one map together. Subprocesses' outputs are NamedSources that can
   * simply be used. they could be kept in a separate map. For each entry, it is essential to look first whether the
   * name is already given and to report any conflict and terminate compilation. What really has to be taken care of,
   * are outputs, variable writings and subprocesses' inputs. In all these cases, generally a formula is being
   * evaluated that generates the value. This formula is a kind of "nameless and stateless subprocess" that in itself
   * is an UnnamedSource. Variables are calculated by formulas, and formulas may contain variables. For this reason,
   * care has to be taken that there are no loops. Before it is evaluated, there must be a check whether this name is
   * already under evaluation. Then, while calculating the variable, its name has to be memorized for this check.
   * Since the loop may go over an arbitrary number of variables, the "name under evaluation" store must fit to
   * contain an unlimited number of variable names. Should the test fail (i.e. an attempt to calculate a variable
   * while it is already being calculated is detected), compilation must throw an error (and additionally set the
   * variable to NaN).
   */

  /* a set that contains all internal names for duplicate check */
  Set<String> usedNames=new TreeSet<String>();
  /* in the future subs and oldObjs shall serve to find illegal stateless loops */
  subs=new ReadOnlyMapImpl<String, NamedSource>();
  // Map<String, Object> oldObjs=new TreeMap<String, Object>();
  int nameCount=0;
  // make all interface inputs known to the external world, and to the internal accounting
  int inputCount=0;
  if(inputs!=null)
  for(ParserVal val:inputs.inner){
    if(usedNames.contains(val.getText()))
    throw new IllegalArgumentException("Process "+name+" contains repeated parameter name "
      +val.getText());
    // TODO check for the right input type. We need a grammar definition for this!
    destinationTypes.put(nameCount, ValueType.STREAM);
    usedNames.add(val.getText());
    namedInputs.put(val.getText(), new Parameter(val.getText(), inputCount++));
    nameCount++;
  }
  // make all variables known to the rest of the process, and to the internal accounting
  if(variables!=null)
  for(ParserVal val:variables.inner){
    if(usedNames.contains(val.getText()))
    throw new IllegalArgumentException("Process "+name+" contains repeated variable name "
      +val.getText());
    Variable v=new Variable();
    v.setAbstractName(val.getText());
    containedVariables.put(val.getText(), v);
    subs.put(val.getText(), v);
    usedNames.add(val.getText());
    SourceStore sst=getAsSource(val.inner.get(0));
    v.setSource(0, sst.source, sst.sourceIndex);
  }
  /*
   * make all internal elements known to the internal accounting these may be process references and in the future
   * also inner process definitions
   */
  if(subprocesses!=null)
  for(ParserVal val:subprocesses.inner){
    NamedSource el;
    if(usedNames.contains(val.getText()))
    throw new IllegalArgumentException("Process "+name+" contains repeated element name "
      +val.getText());
    if(val instanceof ProcessRef){
    ProcessRef r=((ProcessRef)val);
    // TODO find the corresponding process, if it is not a primitive. Should be done during parsing?
    if(r.process==null){
      if(r.localName==null)
        throw new IllegalStateException("no process set for anonymous reference");
      throw new IllegalStateException("no process set for reference "+r.localName);
    }
    el=r.process.createTemplate();
    usedNames.add(r.localName);
    subprocessMap.put(val.getText(), (Process)el);
    subrefMap.put(val.getText(), r);
    subs.put(val.getText(), el);
    break; // do not insert it into subprocess map
    }
    // TODO: allow inner process definitions
    throw new IllegalArgumentException("Element "+val.getText()+" contained in element "
      +getText()
      +" is not of suitable type (must be process reference) in "+filename+", line "+line);
  }
  // TODO: now that we know all sub elements, iterate once again over them, and connect
  // their inputs. All connection counterparts, except for formulas, must be available now.
  // For formulas, anonymous subprocesses must be created on the fly and
  // get their inputs directly fed.
  for(String ename:subs.keySet()){
    // iterate over all subprocesses and subprocess references and connect their inputs
    NamedSource val=subs.get(ename);
    if(val instanceof ProcessRef){
     ProcessRef r=(ProcessRef)val;
     Process p=subprocessMap.get(r.localName);
     if(r.getInputAssignments()!=null)
        for(InputAssignment i:r.getInputAssignments().getAssignments()){
      // now for each input of the subprocess, check if there is an assignment
        if(i.getInputName()==null||i.getInputName().length()==0)
        continue;
      // first, get the destination index to which to connect to
      Parameter dest=p.namedInputs.get(i.getInputName());
      // then for each assignment, check if there is an input, and set the connection
      if(i.getFormula()==null){
        // this input of the sub element stays open, so ignore it
      }else{
        SourceStore sst=getAsSource(i.getFormula());
        p.setSource(dest.i, sst.source, sst.sourceIndex);
      }
        }
     }else if(val instanceof Process){
     throw new IllegalArgumentException("Found process "+val.getAbstractName()+" where a processreference was expected - nested process definitions not yet implemented");
     }else
    // don't expect that here.
     throw new IllegalArgumentException("Found symbol "+val.getAbstractName()+" where a processreference was expected");
   }
  // connect all variables to their value sources
  if(variables!=null)
  for(ParserVal val:variables.inner){
    Variable v=containedVariables.get(val.getText());
    SourceStore sst=getAsSource(val.inner.get(0));
    v.setSource(0, sst.source, sst.sourceIndex);
  }
  // after all internal elements are connected, fix the connections of the outputs.
  // here also, create anonymous subprocesses if applicable.
  for(ParserVal val:outputs.inner){
    if(usedNames.contains(val.getText()))
    throw new IllegalArgumentException("Process "+name+" contains repeated output name "
      +val.getText());
    namedOutputs.put(val.getText(), new Parameter(val.getText(), nameCount));
    // TODO will we ever have process definitions with outputs other than STREAM?
    sourceTypes.put(nameCount, ValueType.STREAM);
    sourceMap.put(nameCount, getAsSource(val));
    usedNames.add(val.getText());
    nameCount++;
  }

  @SuppressWarnings("unused")
  Process process=new Process();

  return null;
  }

  /**
   * Convert the subordinated ParserVal into the corresponding SourceStore linked to ProcessElements
   * The whole substructure of ProcessElements will be created.
   *
   * All Element and ConnectionPoint references will be resolved so that the resulting SourceStore
   * can be queried for values after the first advanceState().
   *
   * This method and createTemplateFormula call each other in two-layer recursion to build a complete
   * tree in case of a formula.
   *
   * @param val
   *          the ParserVal representing a formula to get a corresponding data source for
   * @return the SourceStore that can be queried values for the ParserVal.
   * @throws UnknownConnectionException
   *           if an outer source is not found
   * @throws TypeMismatchException
   *           if an outer source provides unsuitable data
   */
  private SourceStore getAsSource(ParserVal val) throws UnknownConnectionException,
    TypeMismatchException {
  if(val instanceof FormulaElement){
    // nested formula
    return new SourceStore(createTemplateFormula((FormulaElement)val), StandardParameters.OUT.i);
  }
  if(val instanceof ConnectionPoint){
    // named process element output, look in tables
    ConnectionPoint cp=(ConnectionPoint)val;
    if(subs.containsKey(cp.getElementName())){
    NamedSource src=subs.get(cp.getElementName());
    Parameter param=src.outputsByName().get(cp.getConnectionName());
    if(param==null)
      throw new IllegalArgumentException(MessageFormat.format(
        "Element {0} does not have output {1} in {2}", cp.getElementName(),
        cp.getConnectionName(), cp.getLocationText()));
    return new SourceStore(src, param.i);
    }
    throw new UnknownConnectionException("Element "+cp.getElementName()+" in "+name.getText()
      +" not known in "
      +val.filename+" line "+val.line);
  }
  if(val instanceof NamelessSource){
    // a data source that has a stream whose name need not be given. NamelessSources by contract always
    // are NamedSources that connect to OUT
    // NamedSource src=subs.get(val);
    // return new SourceStore(src, StandardParameters.OUT.i);
  }
  if(val instanceof Element){
    // a single name appearing in a formula must refer to an interface input or a process variable
    String elementName=val.getText();
    Parameter param=inputsByName().get(elementName);
    if(param!=null){
    return getInputProxy(elementName);
    }
    Variable v=containedVariables.get(elementName);
    if(v != null){
    return new SourceStore(v, StandardParameters.OUT.i);
    }
    throw new UnknownConnectionException("Input "+elementName+" in "+name.getText()
      +" not known in "
      +val.filename+" line "+val.line);
  }
  if(val instanceof Connection){
    // TODO how to handle?
    Connection c=(Connection)val;
    @SuppressWarnings("unused")
    ConnectionPoint cp=c.source;

    throw new UnknownConnectionException("Connection "+c.getEndpoint()+" in "+name.getText()
      +" not known in "
      +val.filename+" line "+val.line);
  }
  if(val instanceof NamelessSource && val instanceof TemplateProvider){
    return new SourceStore(((TemplateProvider)val).getTemplate().clone(), StandardParameters.OUT.i);
  }
  if(val instanceof ConnectionPoint){
    // can this legally happen?
    ConnectionPoint cp=(ConnectionPoint)val;
    throw new UnknownConnectionException("ConnectionPoint "+
      cp.getElementName()+"."+cp.getConnectionName()+" in "+name.getText()
      +" of unknown type in "
        +val.filename+" line "+val.line);   
  }
  throw new UnknownConnectionException("Element "+val.getText()+" in "+name.getText()
    +" of unknown type in "
      +val.filename+" line "+val.line);
  }

  /**
   * Create a ProcessElement including the sub-elements that together represent a formula. Structure creation is stopped
   * at the formula boundary.
   *
   * This method and getAsSource call each other in two-layer recursion to build a complete tree.
   *
   * @param val
   *          the FormulaElement-type ParserVal representing the top level operation
   * @return the whole ProcessElement tree representing the formula
   * @throws UnknownConnectionException
   *           if an outer source is not found
   * @throws TypeMismatchException
   *           if an outer source provides unsuitable data
   */
  private ProcessElement createTemplateFormula(FormulaElement val)
    throws UnknownConnectionException,
    TypeMismatchException {
  ProcessElement el;
  // FormulaElements always are TemplateProvider-s
  el=((TemplateProvider)val).getTemplate().clone();
  // FormulaElements always are ParserVal-s
  el.setAbstractName(((ParserVal)val).getText());
  if(val instanceof FormulaElement3){
    ParserVal v3=((FormulaElement3)val).getInput3Val();
    SourceStore el3=getAsSource(v3);
    el.setSource(StandardParameters.IN.i, el3.source, el3.sourceIndex);
  }
  if(val instanceof FormulaElement2){
    ParserVal v2=((FormulaElement2)val).getInput2Val();
    SourceStore el2=getAsSource(v2);
    el.setSource(StandardParameters.IN.i, el2.source, el2.sourceIndex);
  }
  if(val instanceof FormulaElement1){
    ParserVal v1=((FormulaElement1)val).getInput1Val();
    SourceStore el1=getAsSource(v1);
    el.setSource(StandardParameters.IN.i, el1.source, el1.sourceIndex);
  }
  return el;
  }

  ProcessElement getTemplate() throws UnknownConnectionException, TypeMismatchException {
  if(template==null)
    template=createTemplate();
  return template;
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.NamedSource#getSourceTypes()
   */
  @Override
  public ReadOnlyMap<Integer, ValueType> getSourceTypes() {
  return sourceTypes;
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.NamedSource#getValue(int)
   */
  @Override
  public double getValue(int index) {
  return sourceMap.get(index).getValue();
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.SampleAdvancer#advanceState()
   */
  @Override
  public void advanceState() {
  // first, mark all variables dirty.
  // whenever a variable is queried, it is only calculated when it is dirty, and then marked clean,
  // so it is never calculated twice in one cycle.
  if(containedVariables!=null)
    for(Variable v:containedVariables.values()){
    v.markDirty();
    }
  for(SampleAdvancer advancer:advancableSubs)
    advancer.advanceState();
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.SampleAdvancer#advanceOutput()
   */
  @Override
  public void advanceOutput() {
  for(SampleAdvancer advancer:advancableSubs)
    advancer.advanceOutput();
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.SampleAdvancer#setAbstractName(java.lang.String)
   */
  @Override
  public void setAbstractName(String abstractName) {
  this.abstractName=abstractName;
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.SampleAdvancer#getAbstractName()
   */
  @Override
  public String getAbstractName() {
  return abstractName;
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.SampleAdvancer#setInstanceName(java.lang.String)
   */
  @Override
  public void setInstanceName(String instanceName) {
  this.instanceName=instanceName;
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.SampleAdvancer#getInstanceName()
   */
  @Override
  public String getInstanceName() {
  return instanceName;
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.NativeStub#getNativeSpace()
   */
  @Override
  public long getNativeSpace() {
  return nativeSpace;
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.ProcessElement#getDestinationTypes()
   */
  @Override
  public ReadOnlyMap<Integer, ValueType> getDestinationTypes() {
  return destinationTypes;
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.ProcessElement#setSource(int, de.maramuse.soundcomp.process.NamedSource, int)
   */
  @Override
  public void setSource(int connectionIndex, NamedSource source, int sourceIndex)
    throws UnknownConnectionException, TypeMismatchException {
  ValueType sourceType=source.getSourceTypes().get(sourceIndex);
  ValueType destType=destinationTypes.get(connectionIndex);
  if(destType==null)
    throw new UnknownConnectionException("Could not determine type of "+name+" input "
      +connectionIndex);
  if(sourceType==null)
    throw new UnknownConnectionException("Could not determine type of "+source.getAbstractName()
      +" output "+sourceIndex);
  if(!sourceType.equals(destType))
    throw new UnknownConnectionException("Connection end types "+name+"/"+connectionIndex+" and "
      +source.getAbstractName()+"/"+sourceIndex+" do not match");
  destinationMap.put(connectionIndex, new SourceStore(source, sourceIndex));
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.ProcessElement#getSourceMap()
   */
  @Override
  public ReadOnlyMap<Integer, SourceStore> getSourceMap() {
  return sourceMap;
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.ProcessElement#outputsByName()
   */
  @Override
  public ReadOnlyMap<String, Parameter> outputsByName() {
  return namedOutputs;
  }

  /*
   * (non-Javadoc)
   *
   * @see de.maramuse.soundcomp.process.ProcessElement#inputsByName()
   */
  @Override
  public ReadOnlyMap<String, Parameter> inputsByName() {
  return namedInputs;
  }

  public Map<String, ProcessElement> getContainedElements() {
  return containedElements;
  }

  /**
   * clone() just creates an "empty" process. The actual content is
   * provided in copyStructure()
   */
  @Override
  public Process clone() {
  return new Process();
  }

  // SourceStore deepCopyFormula(ParserVal val) {
  // SourceStore src=null;
  // NamedSource nsc=null;
  // if(val instanceof Number){
  // nsc=new ConstStream(val.asDouble());
  // src=new SourceStore(nsc, StandardParameters.OUT.i);
  // }else if(val instanceof TemplateProvider){
  // nsc=((TemplateProvider)val).getTemplate();
  // }
  // return src;
  // }

  /**
   * A SourceStore that is used within the InputProxy to allow delayed retrieval of
   * the actual source
   */
  private class InputStore extends SourceStore {
  InputStore(){
    super(null,0);
  }
  }
 
  /**
   * A proxy class that connects internal data destinations to outside sources.
   * We need a proxy because when the internal connections are set up, outside sources are yet unknown.
   * Hierarchical processes definitions are interpreted from inside to outside, so when an inner process
   * definition is handled, no outside structures are available to connect to.
   * These proxies are the central places that later get their inputs connected on first value request.
   */
  private class InputProxy extends ident {
  private String inputName;
  private InputStore store=new InputStore();
  /**
   * Constructor for an input proxy
   *
   * @param inputIndex
   *          the index that the relevant input (data destination) will be seen with from outside
   */
  InputProxy(String inputName) {
    this.inputName=inputName;
  }

  /**
   * Gets a value from the proxied connection. The passed in index is irrelevant as each proxy exactly serves one
   * stream destination
   *
   * The current implementation does not support dis-/reconnection of inputs
   * as the data source is cached upon first use. The control logic must ensure
   * that the inputs of a process are connected before it gets the first
   * advanceState() (which leads to calling getValue below)
   * If there is a need to change this, dis-/reconnecting should look up
   * the relevant input and adjust the proxies.
   */
  @Override
  public double getValue(int index) {
    if(store.source==null){
    Parameter param=namedInputs.get(inputName);
    // TODO when it is possible to define default input values for processes,
    // take care of that here instead of taking Double.NaN.
    if(param==null){
      store.source=ConstStream.c(Double.NaN);
      return Double.NaN;
    }
    SourceStore _store=sourceMap.get(param.i);
    store.sourceIndex=_store.sourceIndex;
    store.source=_store.source;
    }
    return store.getValue();
  }
  }

  public Process setElements(ParserVal list) {
  this.subprocesses=list;
  return this;
  }

 
  // /////////////////////////////////////////////////////////////////////////////////////
  // methods for the control logic, mostly for copying the structure to create
  // concrete instances from the parsed template
  /**
   * create a complete, working copy of this process template. In case of a structure, this is done by recreating it
   * from the parse tree elements.
   *
   * @return a complete, working copy of this process template.
   * @throws Exception
   */
  public Process copyStructure() throws Exception {
  Process dest=clone();
  dest.destinationTypes=destinationTypes;
  dest.sourceTypes=sourceTypes;
  dest.namedOutputs=namedOutputs;
  dest.namedInputs=namedInputs;
  dest.inputs=inputs;
  dest.outputs=outputs;
  dest.subprocesses=subprocesses;
  dest.createTemplate();
  if(name!=null){
    dest.abstractName=name.getText();
    dest.instanceName=name.getText()+copyCount++;
  }else{
    dest.abstractName="anonymous process";
    dest.instanceName="anonymous process"+copyCount++;
  }
  return dest;
  }
 
  public ReadOnlyMap<String, Parameter> getInputs(){
  return namedInputs;
  }
 
  /**
   * retrieve a SourceStore for an input given by textual name
   * @param inputName the name of the input that a SourceStore is requested for
   * @return a SourceStore that can be used to retrieve the input
   */
  public SourceStore getInputProxy(String inputName){
  // TODO: we could cache these proxies in case the same input
  // is used multiple times - it makes no sense duplicating the proxy then
  return new InputProxy(inputName).store;
  }
 
  /**
   * helper class for debugging purposes
   * TODO make this class private and comment out the debug test case
   * when processes can successfully be created from the parser.
   * this will cut off direct external access to the process' internal structures.
   * @return a new empty process.
   */
  public static class DebugProxy {
  Process process;
  public DebugProxy(Process process){
    this.process=process;
  }
 
  public void setInputs(ParserVal inputs){
    process.setInputs(inputs);
  }
 
  public void setOutputs(ParserVal outputs){
    process.setOutputs(outputs);
  }
 
  public static Process create(){
    return new Process();
     }
  }

  @Override
  public ProcessRef getProcessRefByName(String name1) {
  return subrefMap.get(name1);
  }

  @Override
  public boolean containsElement(String name1) {
  return hasVariable(name1)||hasInput(name1)||hasProcessDef(name1);
  }

  @Override
  public boolean hasVariable(String name1) {
  for(ParserVal val:variables.inner){
    if(val.text.equals(name1))return true;
  }
  return false;
  }

  @Override
  public boolean hasInput(String name1) {
  for(ParserVal val:inputs.inner){
    if(val.text.equals(name1))return true;
  }
  return false;
  }

  @Override
  public boolean hasProcessDef(String name1) {
  for(ProcessRef val:subrefMap.values()){
    if(val.localName.equals(name1))return true;
  }
  return false;
  }
}
TOP

Related Classes of de.maramuse.soundcomp.parser.Process$DebugProxy

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.