Package com.sun.tahiti.reader.annotator

Source Code of com.sun.tahiti.reader.annotator.ChoiceAnnotator$Annotator

/*
* @(#)$Id: ChoiceAnnotator.java 1256 2001-10-23 18:27:03Z Bear $
*
* Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the proprietary information of Sun Microsystems, Inc. 
* Use is subject to license terms.
*
*/
package com.sun.tahiti.reader.annotator;

import com.sun.msv.datatype.xsd.StringType;
import com.sun.msv.grammar.*;
import com.sun.msv.grammar.util.ExpressionWalker;
import com.sun.msv.grammar.util.ExpressionPrinter;
import com.sun.msv.grammar.trex.ElementPattern;
import com.sun.msv.grammar.xmlschema.SimpleTypeExp;
import com.sun.tahiti.grammar.*;
import com.sun.tahiti.grammar.util.Multiplicity;
import com.sun.tahiti.grammar.util.MultiplicityCounter;
import com.sun.tahiti.reader.NameUtil;
import java.util.Set;
import java.util.Map;
import java.util.Stack;
import java.util.Iterator;

/**
* annotates <choice> with ClassItem/InterfaceItem
* so that it can be handled easily.
*
* @author
*  <a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a>
*/
class ChoiceAnnotator
{
  private static java.io.PrintStream debug = null;
 
  public static void annotate( AnnotatedGrammar g ) {
   
    ChoiceAnnotator ann = new ChoiceAnnotator(g);
   
    // process all class items.
    ClassItem[] classes = g.getClasses();
    for( int i=0; i<classes.length; i++ )
      classes[i].exp = classes[i].exp.visit(ann.new Annotator(classes[i]));
  }
 
  private ChoiceAnnotator( AnnotatedGrammar g ) {
    this.grammar = g;
  }
 
  /**
   * this map will keep the annotated ReferenceExps. ReferenceExp is the key
   * and annotated result is the value. This map is very important to minimize
   * the number of generated classes/interfaces.
   */
  private final Map annotatedRefs = new java.util.HashMap();

  /** the grammar object to which we are adding annotation. */
  private final AnnotatedGrammar grammar;
 
  /**
   * Annotate the body of one ClassItem.
   */
  private class Annotator extends ExpressionCloner {
   
    private Annotator( ClassItem owner ) {
      super(grammar.getPool());
      this.owner = owner;
    }
   
    /** the current ClassItem object. Its body is what we are dealing with now. */
    private final ClassItem owner;
   
    /**
     * A counter. this value is used to create unique names for added class items.
     */
    private int iota = 0;
   
    public Expression onRef( ReferenceExp exp ) {
     
      // If this ReferenceExp has already visited and annotated. reuse it.
      // This will prevent annotating the same ChoiceExp, etc again and again.
      Expression r = (Expression)annotatedRefs.get(exp);
      if(r!=null)      return r;

      return exp.exp.visit(this);
    }
   
    public Expression onOther( OtherExp exp ) {
      // expands C-C,C-P,C-I relationship.
      if( exp instanceof PrimitiveItem
      ||  exp instanceof InterfaceItem
      ||  exp instanceof ClassItem
      ||  exp instanceof IgnoreItem
      ||  exp instanceof SuperClassItem
      ||  exp instanceof FieldItem )
        return exp;
     
      return exp.exp.visit(this);
    }
   
    public Expression onAttribute( AttributeExp exp ) {
      Expression body = exp.exp.visit(this);
      if( body==exp.exp return exp;
      else  return pool.createAttribute( exp.nameClass, body );
    }
 
    public Expression onElement( ElementExp exp ) {
      /*
      although we will lose any additional information
      added to this ElementExp,
      we have to create a copy of ElementExp. Otherwise
      we cannot correctly process things like:
     
      <define name="X">
        <ref name="Z" t:role="class"/>
      </define>
      <define name="Y">
        <ref name="Z" t:role="field"/>
      </define>
      <define name="Z">
        <element name="foo">
          <data type="string" t:role="primitive"/>
        </element>
      </define>
     
      When processing X->Z, we want to add extra FieldItem.
      When processing Y->Z, we don't want that.
      */
     
      Expression body = exp.contentModel.visit(this);
      if(body==exp.contentModelreturn exp;
      else  return new ElementPattern( exp.getNameClass(), body );
    }

    /**
     * annotate ChoiceExp with FieldItem.
     *
     * <p>
     * children of a ChoiceExp is called "branches". In this method,
     * branches of the entire choice group is considered.
     *
     * <p>
     * We consider a branch is "alive" if there is some JavaItem in that branch.
     * For example, &lt;empty&gt; is not a live branch. IgnoreItem is another
     * example of non-live branch.
     *
     * <p>
     * If only one branch out of the entire branches is alive, then this choice
     * is not treated at all, and the live branch is recursively processed.
     * This handles &lt;optional> p &lt;/optional>.
     *
     * <p>
     * Otherwise, the following algorithm is applied:
     *
     * <p>
     * A branch is said to be "complex", if the multiplicity of child JavaItem is
     * more than one. For example,
     *
     * <PRE><XMP>
     * <oneOrMore>
     *   <tahiti:classItem>
     *     <element name="..."/>
     *       ...
     *     </element>
     *   </tahiti:classItem>
     * </oneOrMore>
     * </XMP></PRE>
     *
     * <p>
     * is a complex branch. If a branch is complex, then it is wrapped by a
     * ClassItem. Wrapping by a ClassItem makes its
     * multiplicity (1,1). This ensures that every branch has the multiplicity
     * of at-most-one.
     */
    public Expression onChoice( ChoiceExp exp ) {
     
      // check whether there is only one meaningul branch, or more than one of them.
      Expression[] b = exp.getChildren();
      boolean[] complexBranch = new boolean[b.length];
      boolean[] fieldlessBranch = new boolean[b.length];
      int numLiveBranch = 0;
     
      boolean bBranchWithField = false;
      final boolean[] bBranchWithPrimitive = new boolean[1];

      if(debug!=null) {
        debug.println( "Processing Choice: " + ExpressionPrinter.printContentModel(exp) );
        debug.println("checking each branch");
      }
     
      for( int i=0; i<b.length; i++ ) {
        final boolean[] hasChildFieldItem = new boolean[1];

        // compute the multiplicity of the all child JavaItems and
        // also compute whether this branch has FieldItem in it.
        Multiplicity m = Multiplicity.calc( b[i],
          new MultiplicityCounter(){
            protected Multiplicity isChild( Expression exp ) {
              if(exp instanceof FieldItemhasChildFieldItem[0] = true;
              if(exp instanceof PrimitiveItembBranchWithPrimitive[0] = true;
             
              if(exp instanceof IgnoreItemreturn Multiplicity.zero;
              if(exp instanceof JavaItem)    return Multiplicity.one;
              else            return null;
            }
          });

        if(debug!=null) {
          debug.println( "  Branch: " + ExpressionPrinter.printContentModel(b[i]) );
          debug.println( "    multiplicity:"+m+"  hasChildFieldItem:"+hasChildFieldItem[0] );
        }
       
        if(m.isZero())
          continue;    // do nothing for this branch.
       
        numLiveBranch++;
       
        if(!m.isAtMostOnce()) {
          // memorize that this branch is complex.
          complexBranch[i] = true;
          continue;
        }
       
        if( !hasChildFieldItem[0] ) {
          // memorize that this branch is fieldless.
          fieldlessBranch[i] = true;
          continue;
        }
       
        bBranchWithField = true;
       
        // this branch has a FieldItem. perform recursion.
        b[i] = b[i].visit(this);
      }
     
      if( numLiveBranch<=1 ) {
        // there is only one meaningful branch.
        // this happens for patterns like <optional>.
       
        // visit all unvisited branch
        for( int i=0; i<b.length; i++ )
          if( fieldlessBranch[i] || complexBranch[i] )
            b[i] = b[i].visit(this);
       
        Expression r = Expression.nullSet;
        for( int i=0; i<b.length; i++ )
          r = pool.createChoice( r, b[i] );
     
        return r;
       
      } else {
       
        /*
        if we don't have any branch with FieldItem, then we just need
        one FieldItem to cover the entire branches.
       
        TODO:(?) actually this would be done better. Even if there are
        branches with FieldItems, one created FieldItem can cover all
        FieldItem-less branches, and then that FieldItem and other
        branches can be combined. But is it an improvement?
        */
       
        for( int i=0; i<b.length; i++ ) {
          if( complexBranch[i] ) {
            if(debug!=null)
              debug.println("  Insert a wrapper class on: "+ExpressionPrinter.printContentModel(exp));
           
            // insert a new class item here.
            String className = owner.getTypeName()+"Subordinate"+(++iota);
           
            ClassItem ci = grammar.createClassItem( className, b[i].visit(this) );
            ci.isTemporary = true;
            b[i] = ci;
          }
        }
     
        Expression r = Expression.nullSet;
        for( int i=0; i<b.length; i++ )
          r = pool.createChoice( r, b[i] );

        if( !bBranchWithField ) {
          // there was no branch with FieldItem.
         
          if( !bBranchWithPrimitive[0] ) {
            // if there is no branch with a PrimitiveItem,
            // add an interface item automatically.
           
            // compute the interface name
            String packagePrefix = owner.getPackageName();
            if(packagePrefix==nullpackagePrefix="";
            else          packagePrefix+=".";
           
            String intfName = "I"+owner.getBareName()+"Content";
           
            if(grammar.interfaces.containsKey(packagePrefix+intfName)) {
              // the last resort
              int cnt = 2;
              while( grammar.interfaces.containsKey(packagePrefix+intfName+cnt) )
                cnt++;
              intfName = intfName + cnt;
            }
         
            if(debug!=null) {
              debug.println("  Wrap it by an interface iem: "+packagePrefix+intfName);
              debug.println("  "+ ExpressionPrinter.printContentModel(r) );
            }
            r = grammar.createInterfaceItem( packagePrefix+intfName, r );
          }
        }
        return r;
      }
    }
 
   
    private RuntimeException eureka = new RuntimeException();
   
    public Expression onMixed( MixedExp exp ) {
     
      /*
      expand this <mixed> as
      <interleave>
        <zeroOrMore>
          <primitiveItem>
            <data type="string"/>
          </primitiveItem>
        </zeroOrMore>
        exp.exp
      </interleave>
      */
      Expression expanded = pool.createInterleave(
          pool.createZeroOrMore(
            new PrimitiveItem( StringType.theInstance,
              pool.createData(StringType.theInstance) )),
          exp.exp );
     
      try {
        // see if "exp" contains FieldItem.
        exp.exp.visit( new ExpressionWalker(){
          public void onOther( OtherExp exp ) {
            if(exp instanceof FieldItemthrow eureka;
            if(exp instanceof JavaItem)    return;
            super.onOther(exp);
          }
        });
      } catch(RuntimeException e) {
        // if there is a field item in it,
        // then visit "expanded".
        // typically this will loose the order between characters and
        // elements, but this seems to be a reasonable compromise.
        assert(e==eureka);
        return expanded.visit(this);
      }
     
      return expanded;
    }
   
  }
   
  private static void assert( boolean b ) {
    if(!bthrow new Error();
  }
}
TOP

Related Classes of com.sun.tahiti.reader.annotator.ChoiceAnnotator$Annotator

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.