Revision: 15232
          http://gate.svn.sourceforge.net/gate/?rev=15232&view=rev
Author:   valyt
Date:     2012-01-26 18:04:09 +0000 (Thu, 26 Jan 2012)
Log Message:
-----------
Getting close to being able to generate some Java-like strings...

Modified Paths:
--------------
    futures/JAPE_PlusC/src/gate/jape/plus/SPTBase.java
    futures/JAPE_PlusC/src/gate/jape/plus/SPTBuilder.java

Added Paths:
-----------
    futures/JAPE_PlusC/src/gate/jape/plus/SPTPhaseEG.java

Modified: futures/JAPE_PlusC/src/gate/jape/plus/SPTBase.java
===================================================================
--- futures/JAPE_PlusC/src/gate/jape/plus/SPTBase.java  2012-01-26 16:45:32 UTC 
(rev 15231)
+++ futures/JAPE_PlusC/src/gate/jape/plus/SPTBase.java  2012-01-26 18:04:09 UTC 
(rev 15232)
@@ -53,10 +53,12 @@
 /**
  * An optimised implementation for a JAPE single phase transducer.
  */
-public class SPTBase extends AbstractLanguageAnalyser {
+public abstract class SPTBase extends AbstractLanguageAnalyser {
 
   protected ActionContext actionContext;
   
+  protected abstract void advanceInstance(FSMInstance instance);
+  
   /**
    * Sets the action context to be used during execution of RHS actions.
    * @param ac
@@ -309,16 +311,16 @@
    * An array containing all the annotation types that are relevant to this
    * transducer (that is, all the types mentioned in rules).
    */
-  protected String[] annotationTypes;
+  protected final String[] annotationTypes;
 
   /**
    * Stores all the atomic predicates used in this transducer. For each
    * annotation type, an arrays is kept, with predicates relevant to that
    * annotation type.
    */
-  protected Predicate[][] predicatesByType;
+  protected final Predicate[][] predicatesByType;
 
-  protected String phaseName;
+  protected final String phaseName;
   
   /**
    * The pseudo-type used for annotations that are of a type not mentioned in
@@ -397,7 +399,7 @@
    * is consumed during the traversal. This array refers
    * {@link SinglePhaseTransducerPDA}.getFSM().getBindingNames().
    */
-  protected String[] arrayOfBindingNames;
+  protected final String[] bindingNames;
 
   /**
    * Used for counting the number of predicate hits (cases where checking a 
@@ -417,24 +419,24 @@
   /**
    * The set of rules in this transducer.
    */
-  protected Rule[] rules;
+  protected final Rule[] rules;
 
   /**
    * The type of matching used for this transducer.
    */
-  protected MatchMode matchMode;
+  protected final MatchMode matchMode;
 
   /**
    * Should the transducer log warnings when multiple matches are possible in
    * Appelt mode?
    */
-  protected boolean debugMode;
+  protected final boolean debugMode;
 
   /**
    * Should the transducer apply all possible matches when multiple matches are
    * possible in Appelt mode?
    */
-  protected boolean groupMatchingMode;
+  protected final boolean groupMatchingMode;
 
   /**
    * The list of active FSM instances.
@@ -447,9 +449,24 @@
   protected List<FSMInstance> acceptingInstances;
 
   
-  public SPTBase() {
+  protected SPTBase(String phaseName, 
+                    String[] bindingNames, 
+                    String[] annotationTypes,
+                    boolean debugMode,
+                    boolean groupMatchingMode, 
+                    MatchMode matchMode, 
+                    Rule[] rules,
+                    Predicate[][] predicatesByType) {
     percentFormat = NumberFormat.getPercentInstance();
     percentFormat.setMinimumFractionDigits(3);
+    this.phaseName = phaseName;
+    this.bindingNames = bindingNames;
+    this.annotationTypes = annotationTypes;
+    this.debugMode = debugMode;
+    this.groupMatchingMode = groupMatchingMode;
+    this.matchMode = matchMode;
+    this.rules = rules;
+    this.predicatesByType = predicatesByType;
   }
   
   
@@ -1039,7 +1056,7 @@
       // start with state 0
       activeInstances.add(new FSMInstance(currentAnnotation, 0,
               new HashMap<String, IntArrayList>()));      
-      activeInstances: while(activeInstances.size() > 0) {
+      while(activeInstances.size() > 0) {
         if(owner.isInterrupted()) throw new ExecutionInterruptedException(
                 "The execution of the \"" + getName() + 
                 "\" JAPE Plus transducer has been interrupted!");
@@ -1048,123 +1065,7 @@
         // advance the top instance, and queue all resulting instances.
         // get the first instance
         FSMInstance fsmInstance = activeInstances.removeFirst();
-        if(states[fsmInstance.state].rule >= 0) {
-          // current instance is in a final state
-          acceptingInstances.add(fsmInstance.clone());
-          if(matchMode == MatchMode.FIRST || matchMode == MatchMode.ONCE) {
-            break activeInstances;
-          }
-        }
-        // check all transitions and advance in all possible ways.
-        transitions: for(Transition aTransition : 
states[fsmInstance.state].transitions) {
-          if(aTransition.type == TransitionPDA.TYPE_OPENING_ROUND_BRACKET){
-            //opening-round-bracket transition
-            FSMInstance nextInstance = fsmInstance.clone();
-            nextInstance.pushNewEmptyBindingSet();
-            nextInstance.state = aTransition.nextState;
-            activeInstances.addLast(nextInstance);
-            // we do not advance the annotation index,
-            // since opening-round-bracket
-            // transitions are treated like epsilon transitions
-            continue transitions;
-          }
-          if(aTransition.type != TransitionPDA.TYPE_CONSTRAINT){
-            // closing-round-bracket transition
-            FSMInstance nextInstance = fsmInstance.clone();
-            nextInstance.popBindingSet(arrayOfBindingNames[aTransition.type]);
-            nextInstance.state = aTransition.nextState;
-            activeInstances.addLast(nextInstance);
-            // we do not advance the annotation index,
-            // since closing-round-bracket
-            // transitions are treated like epsilon transitions
-            continue transitions;
-          }
-          // constrained transition;
-          // for each constraint, for each annotation, see if they match
-          // this stores a list of candidate annotations for each constraint
-          IntArrayList[] annotsForConstraints =
-                  new IntArrayList[aTransition.constraints.length];
-          for(int constraintIdx = 0; 
-              constraintIdx < aTransition.constraints.length; 
-              constraintIdx++) {
-            int[] constraint = aTransition.constraints[constraintIdx];
-            annotations: for(int annIdx = fsmInstance.annotationIndex; 
-                annIdx < annotation.length &&
-                annIdx < annotationNextOffset[fsmInstance.annotationIndex]; 
-                annIdx++) {
-              if(constraint[0] == annotationType[annIdx]) {
-                // type matched, now check predicates;
-                for(int predIdx = 2; predIdx < constraint.length; predIdx++) {
-                  if(!checkPredicate(annIdx, constraint[predIdx])) {
-                    // one predicate failed -> move to next annotation
-                    continue annotations;
-                  }
-                }
-                // if we got this far, all predicates succeeded, so this
-                // annotation matches -> add it to the list for the current
-                // constraint
-                if(annotsForConstraints[constraintIdx] == null) {
-                  annotsForConstraints[constraintIdx] = new IntArrayList();
-                }
-                annotsForConstraints[constraintIdx].add(annIdx);
-              }
-            }
-            // we just finished checking one constraint
-            if(constraint[1] < 0){
-              //constraint is negated
-              if(annotsForConstraints[constraintIdx] == null){
-                //no annotations matched -> constraint succeeds!
-                annotsForConstraints[constraintIdx] = new IntArrayList();
-                // no annotation is bound though!
-                annotsForConstraints[constraintIdx].add(-1);
-              }else{
-                //annotation were matched -> so the negated constraint fails!
-                continue transitions;
-              }
-            }else{
-              if(annotsForConstraints[constraintIdx] == null) {
-                // current constraint matched nothing -> transition failed.
-                continue transitions;
-              }
-            }
-          }// for: constraints
-          // we finished checking all constraints, and they all succeeded
-          // -> apply the transition with all possible bindings combinations
-          // a next step is a set of bound annotations, one for each constraint
-          List<int[]> nextSteps = enumerateCombinations(annotsForConstraints);
-          for(int[] aStep : nextSteps) {
-            FSMInstance nextInstance = fsmInstance.clone();
-            // update the data in the next instance            
-            nextInstance.state = aTransition.nextState;
-            // Calculate the next annotation to look at: find the one starting
-            // after the longest matched annotation.
-            int nextAnnotationForInstance = fsmInstance.annotationIndex;
-            int maxNextStep = -1;
-            for(int i = 0; i < aStep.length; i++) {
-              if(aStep[i]>= 0){
-                if(maxNextStep < aStep[i]) maxNextStep = aStep[i];
-                if(nextAnnotationForInstance < followingAnnotation(aStep[i])) {
-                  nextAnnotationForInstance = followingAnnotation(aStep[i]);
-                }
-              }
-            }
-            // When zero-length annotations are used, 
followingAnnotation(annIDx)
-            // may not actually advance. To avoid infinite looping, we need to 
-            // make sure that next annotation is greater than all the ones
-            // already matched. 
-            if(nextAnnotationForInstance <= maxNextStep) {
-              if(maxNextStep < annotation.length -2) {
-                nextAnnotationForInstance = maxNextStep + 1;
-              } else {
-                // no more annotations
-                nextAnnotationForInstance = Integer.MAX_VALUE;
-              }
-            }
-            nextInstance.annotationIndex = nextAnnotationForInstance;
-            nextInstance.bindAnnotations(aStep);
-            activeInstances.addLast(nextInstance);
-          }
-        }// for transitions
+        advanceInstance(fsmInstance);
       }// while activeInstances not empty
       // at this point, there are no more active instances (or we exited due to
       // matching mode being First or Once).

Modified: futures/JAPE_PlusC/src/gate/jape/plus/SPTBuilder.java
===================================================================
--- futures/JAPE_PlusC/src/gate/jape/plus/SPTBuilder.java       2012-01-26 
16:45:32 UTC (rev 15231)
+++ futures/JAPE_PlusC/src/gate/jape/plus/SPTBuilder.java       2012-01-26 
18:04:09 UTC (rev 15232)
@@ -15,6 +15,9 @@
 
 package gate.jape.plus;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -42,6 +45,7 @@
 import gate.jape.constraint.ContainsPredicate;
 import gate.jape.constraint.WithinPredicate;
 import gate.jape.plus.Predicate.PredicateType;
+import gate.jape.plus.SPTBase.FSMInstance;
 import gate.jape.plus.SPTBase.MatchMode;
 
 /**
@@ -91,55 +95,119 @@
     createNewStates(fsm);
     createNewTransitions(fsm);
     optimisePredicates();
+
+    //TODO: generate code, compile class, load instance
     
-    SPTBase optimisedTransducer = new SPTBase();
-    optimisedTransducer.phaseName = oldSpt.getName();
-    optimisedTransducer.arrayOfBindingNames = fsm.getBindingNames();
-    //annotation types
-    optimisedTransducer.annotationTypes = new String[annotationTypes.size()];
-    optimisedTransducer.annotationTypes = annotationTypes.toArray(
-            optimisedTransducer.annotationTypes);
-    //options
-    optimisedTransducer.debugMode = oldSpt.isDebugMode();
-    optimisedTransducer.groupMatchingMode = oldSpt.isMatchGroupMode();
-    //input types
-    Set<String> inputTypes = oldSpt.input;
-    optimisedTransducer.inputAnnotationTypes = 
-        inputTypes == null || inputTypes.size() == 0 ? 
-        null : new String[inputTypes.size()];
-    if(optimisedTransducer.inputAnnotationTypes != null){
-      optimisedTransducer.inputAnnotationTypes = inputTypes.toArray(
-              optimisedTransducer.inputAnnotationTypes);
+    StringBuilder sptCode = new StringBuilder();
+    String className = oldSpt.getName();
+    // append preamble
+    
+    // write constructor
+    sptCode.append("public void ");
+    sptCode.append(className);
+    sptCode.append(" (Rule[] rules, Predicate[][] predicatesByType) {\n");
+    sptCode.append("super (");
+    // phaseName
+    sptCode.append(oldSpt.getName());
+    sptCode.append(",\n");
+    // binding names
+    sptCode.append("// binding names");
+    sptCode.append("new String[]{");
+    boolean first = true;
+    for(String str : fsm.getBindingNames()) {
+      if(first) {
+        first = false;
+      } else {
+        sptCode.append(", ");
+      }
+      sptCode.append("\"" + str +"\"");
     }
-    //match style
+    sptCode.append("},\n");
+    // annotationTypes
+    sptCode.append("// annotationTypes");
+    sptCode.append("new String[]{");
+    first = true;
+    for(String str : annotationTypes) {
+      if(first) {
+        first = false;
+      } else {
+        sptCode.append(", ");
+      }
+      sptCode.append("\"" + str +"\"");
+    }
+    sptCode.append("},\n");
+    // debugMode
+    sptCode.append(oldSpt.isDebugMode());
+    sptCode.append(",\n");
+    // groupMatchingMode
+    sptCode.append(oldSpt.isMatchGroupMode());
+    sptCode.append(",\n");
+    // match style
+    String matchStyle = MatchMode.APPELT.name(); // default is APPELT
     if(oldSpt.getRuleApplicationStyle() == JapeConstants.ALL_STYLE){
-      optimisedTransducer.matchMode = MatchMode.ALL;
-    }else if(oldSpt.getRuleApplicationStyle() == JapeConstants.APPELT_STYLE){
-      optimisedTransducer.matchMode = MatchMode.APPELT;
-    }else if(oldSpt.getRuleApplicationStyle() == JapeConstants.BRILL_STYLE){
-      optimisedTransducer.matchMode = MatchMode.BRILL;
+      matchStyle = MatchMode.ALL.name();
+    } else if(oldSpt.getRuleApplicationStyle() == JapeConstants.BRILL_STYLE){
+      matchStyle =  MatchMode.BRILL.name();
     }else if(oldSpt.getRuleApplicationStyle() == JapeConstants.FIRST_STYLE){
-      optimisedTransducer.matchMode = MatchMode.FIRST;
+      matchStyle =  MatchMode.FIRST.name();
     }else if(oldSpt.getRuleApplicationStyle() == JapeConstants.ONCE_STYLE){
-      optimisedTransducer.matchMode = MatchMode.ONCE;
+      matchStyle =  MatchMode.ONCE.name();
     }
+    sptCode.append(matchStyle);
+    sptCode.append(",\n");
+    sptCode.append("rules, predicatesByType\n");
+    
+    sptCode.append(")\n"); // end super()
+    sptCode.append("}\n"); // end constructor
+    // compile the class
+    Class<? extends SPTBase> sptClass = null; //TODO
+
+    //prepare the parameters for calling the constructor
     //predicates
-    optimisedTransducer.predicatesByType = new Predicate[
-        optimisedTransducer.annotationTypes.length][];
-    for(int i = 0; i < optimisedTransducer.predicatesByType.length; i++){
-      String annType = optimisedTransducer.annotationTypes[i];
+    Predicate[][] predicatesByTypeArray = new Predicate[
+        annotationTypes.size()][];
+    for(int i = 0; i < predicatesByTypeArray.length; i++){
+      String annType = annotationTypes.get(i);
       List<Predicate> preds = predicatesByType.get(annType);
       if(preds != null){
-        optimisedTransducer.predicatesByType[i] = new Predicate[preds.size()];
-        optimisedTransducer.predicatesByType[i] = preds.toArray(
-                optimisedTransducer.predicatesByType[i]);
+        predicatesByTypeArray[i] = new Predicate[preds.size()];
+        predicatesByTypeArray[i] = preds.toArray(
+          predicatesByTypeArray[i]);
       }else{
-        optimisedTransducer.predicatesByType[i] = new Predicate[0];
+        predicatesByTypeArray[i] = new Predicate[0];
       }
     }
-    // rules
-    optimisedTransducer.rules = rules;
-    rules = null;
+    SPTBase optimisedTransducer = null;
+    try {
+      Constructor<? extends SPTBase> sptConstructor = sptClass.getConstructor(
+        Rule[].class, Predicate[][].class);
+      optimisedTransducer = sptConstructor.newInstance(rules, 
predicatesByTypeArray);
+    } catch(SecurityException e) {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    } catch(NoSuchMethodException e) {
+      throw new ResourceInstantiationException(e);
+    } catch(IllegalArgumentException e) {
+      throw new ResourceInstantiationException(e);
+    } catch(InstantiationException e) {
+      throw new ResourceInstantiationException(e);
+    } catch(IllegalAccessException e) {
+      throw new ResourceInstantiationException(e);
+    } catch(InvocationTargetException e) {
+      throw new ResourceInstantiationException(e);
+    }
+    
+    //input types
+    Set<String> inputTypes = oldSpt.input;
+    optimisedTransducer.inputAnnotationTypes = 
+        inputTypes == null || inputTypes.size() == 0 ? 
+        null : new String[inputTypes.size()];
+    if(optimisedTransducer.inputAnnotationTypes != null){
+      optimisedTransducer.inputAnnotationTypes = inputTypes.toArray(
+              optimisedTransducer.inputAnnotationTypes);
+    }
+    
+    
     //states
     optimisedTransducer.states = new SPTBase.State[newStates.size()];
     optimisedTransducer.states = newStates.toArray(optimisedTransducer.states);

Added: futures/JAPE_PlusC/src/gate/jape/plus/SPTPhaseEG.java
===================================================================
--- futures/JAPE_PlusC/src/gate/jape/plus/SPTPhaseEG.java                       
        (rev 0)
+++ futures/JAPE_PlusC/src/gate/jape/plus/SPTPhaseEG.java       2012-01-26 
18:04:09 UTC (rev 15232)
@@ -0,0 +1,189 @@
+package gate.jape.plus;
+
+import gate.jape.Rule;
+
+import java.util.List;
+
+import static gate.jape.plus.SPTBase.MatchMode.*;
+
+import cern.colt.list.IntArrayList;
+
+public class SPTPhaseEG extends SPTBase {
+  
+  public SPTPhaseEG(Rule[] rules, Predicate[][] predicatesByType) {
+    super("$phaseName", 
+      new String[] {"$bindingNames"},
+      new String[] {"$annotationTypes"},
+      false, 
+      false, 
+      APPELT, 
+      rules, predicatesByType);
+  }
+
+
+  @Override
+  protected final void advanceInstance(FSMInstance instance) {
+    //TODO
+    switch(instance.state) {
+      case 0: 
+        state0(instance);
+        break;
+      case 1: 
+        state1(instance);
+        break;
+      case 2: 
+        state2(instance);
+        break;
+      case 3: 
+        state3(instance);
+        break;
+    }
+  }
+  
+  
+  private final void state0(FSMInstance instance) {
+    if(states[instance.state].rule >= 0) {
+      // current instance is in a final state
+      acceptingInstances.add(instance.clone());
+      if (matchMode == MatchMode.FIRST || matchMode == MatchMode.ONCE) {
+        // we're done!
+        //TODO?
+        return;
+      }
+    }
+    // advance
+    // for each transition
+    
+    // example opening-round-bracket transition    
+    {
+      int nextState  = 23; //TODO
+      FSMInstance nextInstance = instance.clone();
+      nextInstance.pushNewEmptyBindingSet();
+      nextInstance.state = nextState;
+      activeInstances.addLast(nextInstance);
+    }
+    
+    // example closing-round-bracket transition
+    {
+      int nextState  = 23; //TODO
+      int transitionType = 2; //TODO
+      FSMInstance nextInstance = instance.clone();
+      nextInstance.popBindingSet(bindingNames[transitionType]);
+      nextInstance.state = nextState;
+      activeInstances.addLast(nextInstance);
+      // we do not advance the annotation index,
+      // since closing-round-bracket
+      // transitions are treated like epsilon transitions
+    }
+    
+    
+    // constrained transition;
+    {
+      // for each constraint, for each annotation, see if they match
+      // this stores a list of candidate annotations for each constraint
+      int constraintsLength = 3; //TODO
+      int nextState  = 23; //TODO
+      IntArrayList[] annotsForConstraints =
+              new IntArrayList[constraintsLength];
+      // TODO: unloop 
+//      for(int constraintIdx = 0; 
+//          constraintIdx < constraintsLength; 
+//          constraintIdx++) {
+//        int[] constraint = aTransition.constraints[constraintIdx];
+      blklbl0001:do{  // example for each constraint loop 
+        int constraintIdx = 0; // TODO
+        int[] constraint = new int[] {1, 2, 3}; //TODO
+        annotations: for(int annIdx = instance.annotationIndex; 
+            annIdx < annotation.length &&
+            annIdx < annotationNextOffset[instance.annotationIndex]; 
+            annIdx++) {
+          if(constraint[0] == annotationType[annIdx]) {
+            // type matched, now check predicates;
+            for(int predIdx = 2; predIdx < constraint.length; predIdx++) {
+              if(!checkPredicate(annIdx, constraint[predIdx])) {
+                // one predicate failed -> move to next annotation
+                continue annotations;
+              }
+            }
+            // if we got this far, all predicates succeeded, so this
+            // annotation matches -> add it to the list for the current
+            // constraint
+            if(annotsForConstraints[constraintIdx] == null) {
+              annotsForConstraints[constraintIdx] = new IntArrayList();
+            }
+            annotsForConstraints[constraintIdx].add(annIdx);
+          }
+        }
+        // we just finished checking one constraint
+        if(constraint[1] < 0){
+          //constraint is negated
+          if(annotsForConstraints[constraintIdx] == null){
+            //no annotations matched -> constraint succeeds!
+            annotsForConstraints[constraintIdx] = new IntArrayList();
+            // no annotation is bound though!
+            annotsForConstraints[constraintIdx].add(-1);
+          }else{
+            //annotation were matched -> so the negated constraint fails!
+            // continue transitions; TODO?
+            break blklbl0001;
+          }
+        }else{
+          if(annotsForConstraints[constraintIdx] == null) {
+            // current constraint matched nothing -> transition failed.
+            // continue transitions; TODO?
+            break blklbl0001;
+          }
+        }
+      } while (false);
+//      }// for: constraints
+      // we finished checking all constraints, and they all succeeded
+      // -> apply the transition with all possible bindings combinations
+      // a next step is a set of bound annotations, one for each constraint
+      List<int[]> nextSteps = enumerateCombinations(annotsForConstraints);
+      for(int[] aStep : nextSteps) {
+        FSMInstance nextInstance = instance.clone();
+        // update the data in the next instance            
+        nextInstance.state = nextState;
+        // Calculate the next annotation to look at: find the one starting
+        // after the longest matched annotation.
+        int nextAnnotationForInstance = instance.annotationIndex;
+        int maxNextStep = -1;
+        for(int i = 0; i < aStep.length; i++) {
+          if(aStep[i]>= 0){
+            if(maxNextStep < aStep[i]) maxNextStep = aStep[i];
+            if(nextAnnotationForInstance < followingAnnotation(aStep[i])) {
+              nextAnnotationForInstance = followingAnnotation(aStep[i]);
+            }
+          }
+        }
+        // When zero-length annotations are used, followingAnnotation(annIDx)
+        // may not actually advance. To avoid infinite looping, we need to 
+        // make sure that next annotation is greater than all the ones
+        // already matched. 
+        if(nextAnnotationForInstance <= maxNextStep) {
+          if(maxNextStep < annotation.length -2) {
+            nextAnnotationForInstance = maxNextStep + 1;
+          } else {
+            // no more annotations
+            nextAnnotationForInstance = Integer.MAX_VALUE;
+          }
+        }
+        nextInstance.annotationIndex = nextAnnotationForInstance;
+        nextInstance.bindAnnotations(aStep);
+        activeInstances.addLast(nextInstance);
+      }
+    }
+  }
+  
+  private final void state1(FSMInstance instance) {
+    
+  }
+  
+  private final void state2(FSMInstance instance) {
+    
+  }
+  
+  private final void state3(FSMInstance instance) {
+    
+  }
+}


Property changes on: futures/JAPE_PlusC/src/gate/jape/plus/SPTPhaseEG.java
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
Keep Your Developer Skills Current with LearnDevNow!
The most comprehensive online learning library for Microsoft developers
is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3,
Metro Style Apps, more. Free future releases when you subscribe now!
http://p.sf.net/sfu/learndevnow-d2d
_______________________________________________
GATE-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/gate-cvs

Reply via email to