package runtime;
import org.relaxng.datatype.ValidationContext;
import org.xml.sax.Attributes;
/**
* Set of attributes and their values.
*
* @author Kohsuke Kawaguchi (kk@kohsuke.org)
*/
public final class AttributesSet {
/** Number of attributes in this set. */
private int size =0;
/** Encoded names of attributes. */
private int[] names = new int[16];
/** Values of attributes. */
private String[] values = new String[16];
/** Context object, in which datatypes evaluate values. */
private ValidationContext context;
private final static int CACHE_SIZE = 37;
/**
* States that are known to be valid wrt the attribute set.
* Attribute values are often evaluated multiple times against
* the same state, so this works as a cache to improve performance
* in those cases.
*
* <p>
* This is used as a hashtable from the hash code.
*/
private Transition.Att[] transitionCache = emptyCache;
private final Transition.Att[] privateCache = new Transition.Att[CACHE_SIZE];
private static final Transition.Att[] emptyCache = new Transition.Att[CACHE_SIZE];
public AttributesSet() {}
/** Re-initializes attributes. */
public void reset( ValidateletImpl validatelet, Attributes atts, ValidationContext _context ) {
size = atts.getLength();
if( names.length<size ) {
// buffer to small. reallocate the buffer
names = new int[size];
values = new String[size];
}
for( int i=size-1; i>=0; i-- ) {
NameCodeMap.Entry e;
names[i] = validatelet.getNameCode( atts.getURI(i), atts.getLocalName(i) ).nameCode;
values[i] = atts.getValue(i);
}
this.transitionCache = emptyCache;
this.context = _context;
}
public int size() { return size; }
// public ValidationContext getContext() { return context; }
public int getName( int idx ) { return names[idx]; }
public String getValue( int idx ) { return values[idx]; }
/**
* Returns true if the attribute transition can be taken
* with respect to this attribute set.
*/
public boolean matchs( Transition.Att aa, StateFactory factory ) {
int matchCount=0,failCount=0;
// check the cache table
if( transitionCache[ (aa.hashCode()&0x7FFFFFFF)%CACHE_SIZE ]==aa )
return true; // cache hit
for( int j=size-1; j>=0; j-- ) {
if( aa.accepts(names[j]) ) {
String value = values[j];
State s = aa.left.text(
value, value.trim().length()==0,
context, empty,
State.emptySet, factory );
if( s.isFinal() ) matchCount++;
else failCount++;
}
}
if( (matchCount==1 && failCount==0 && !aa.repeated)
|| (matchCount!=0 && failCount==0 && aa.repeated) ) {
if( transitionCache==emptyCache ) {
// reset the transition cache
transitionCache = privateCache;
System.arraycopy(emptyCache,0,privateCache,0,CACHE_SIZE);
// for( int i=CACHE_SIZE-1; i>=0; i-- )
// transitionCache[i] = null;
}
transitionCache[ (aa.hashCode()&0x7FFFFFFF)%CACHE_SIZE ] = aa; // update the cache entry
return true;
} else
return false;
}
/** Empty attributes available as a constant. */
public final static AttributesSet empty = new AttributesSet();
}