/*
* @(#)$Id: ExpressionAcceptor.java,v 1.44 2002/10/18 02:50:05 kk122374 Exp $
*
* 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.msv.verifier.regexp;
import com.sun.msv.grammar.*;
import com.sun.msv.grammar.IDContextProvider;
import com.sun.msv.verifier.*;
import com.sun.msv.util.StartTagInfo;
import com.sun.msv.util.StringRef;
import com.sun.msv.util.DatatypeRef;
import org.relaxng.datatype.Datatype;
import org.relaxng.datatype.DatatypeException;
import java.util.*;
/**
* {@link Acceptor} implementation.
*
* <p>
* When you are using <code>REDocumentDeclaration</code>, then the acceptor
* is always guaranteed to be a subclass of this class.
*
* Therefore, by using this regexp implementation of VGM, you can always downcast
* {@link Acceptor} to this class and access its contents to get more information.
*
* <p>
* If you consider VGM as an automaton,
* this class can be thought as a lazy automaton acceptor.
*
*
* @author <a href="mailto:kohsuke.kawaguchi@eng.sun.com">Kohsuke KAWAGUCHI</a>
*/
public abstract class ExpressionAcceptor implements Acceptor {
private Expression expression;
/**
* gets the residual content model.
*
* <p>
* This method returns the expression that represents the expected content model
* it will read.
* For example, if the original content model is (A,(B|C)) and this acceptor
* has already read A, then this method returns (B|C).
*
* <p>
* The returned residual is useful to find out what elements can appear next.
*
* <p>
* If you consider VGM as an automaton, the residual content model
* can be thought as the current state. Also,
* At the same time, right language (a regular expression that represents
* the language it can accept from now on).
*/
public Expression getExpression() { return expression; }
/** this object provides various function objects */
protected final REDocumentDeclaration docDecl;
/**
* If true, this acceptor will ignore all undeclared attributes.
* If false, this acceptor will signal an error for an undeclared attribute.
*
* <p>
* This flag is used to implement the semantics of RELAX Core, where
* undeclared attributes are allowed.
*/
protected final boolean ignoreUndeclaredAttributes;
public ExpressionAcceptor( REDocumentDeclaration docDecl, Expression exp, boolean ignoreUndeclaredAttributes ) {
this.docDecl = docDecl;
this.expression = exp;
this.ignoreUndeclaredAttributes = ignoreUndeclaredAttributes;
}
/**
* creates combined child acceptor and primitive child acceptors (if necessary).
*
* be careful not to keep returned object too long because
* it is reused whenever the method is called.
*
* @return null
* if errRef is null and this expression cannot accept given start tag.
* if errRef is non-null and error recovery is not possible.
*/
public Acceptor createChildAcceptor( StartTagInfo tag, StringRef errRef ) {
final CombinedChildContentExpCreator cccc = docDecl.cccec;
// obtains fully combined child content pattern
CombinedChildContentExpCreator.ExpressionPair e = cccc.get(expression,tag);
if( e.content!=Expression.nullSet ) {
// successful.
if( com.sun.msv.driver.textui.Debug.debug ) {
System.out.println("accept start tag <"+ tag.qName+">. combined content pattern is");
System.out.println(com.sun.msv.grammar.util.ExpressionPrinter.printContentModel(e.content));
if( e.continuation!=null )
System.out.println("continuation is:\n"+
com.sun.msv.grammar.util.ExpressionPrinter.printContentModel(e.continuation)
);
else
System.out.println("no continuation");
}
return createAcceptor( e.content, e.continuation,
cccc.getMatchedElements(), cccc.numMatchedElements() );
}
// no element declaration is satisfied by this start tag.
// this must be an error of input document.
if( errRef==null )
// bail out now to notify the caller that an error was found.
return null;
// no ElementExp accepts this tag name
// (actually, some ElementExp may have possibly accepted this tag name,
// but as a result of <concur>, no expression left ).
errRef.str = diagnoseBadTagName(tag);
if( errRef.str==null )
// no detailed error message was prepared.
// use some generic one.
errRef.str = docDecl.localizeMessage( docDecl.DIAG_BAD_TAGNAME_GENERIC, tag.qName );
// prepare child acceptor.
return createRecoveryAcceptors();
}
protected abstract Acceptor createAcceptor(
Expression contentModel, Expression continuation/*can be null*/,
ElementExp[] primitives, int numPrimitives );
public final boolean onAttribute(
String namespaceURI, String localName, String qName, String value,
IDContextProvider context, StringRef refErr, DatatypeRef refType ) {
// instead of creating a new object each time,
// use a cached copy.
docDecl.attToken.reinit( namespaceURI,localName,qName,
new StringToken(docDecl,value,context,refType) );
return onAttribute( docDecl.attToken, refErr );
}
protected boolean onAttribute( AttributeToken token, StringRef refErr ) {
Expression r = docDecl.attFeeder.feed( this.expression, token, ignoreUndeclaredAttributes );
if( r!=Expression.nullSet ) {
// this attribute is properly consumed.
expression = r;
if(com.sun.msv.driver.textui.Debug.debug)
System.out.println("-- residual after :" +
com.sun.msv.grammar.util.ExpressionPrinter.printContentModel(r));
return true;
}
if( refErr==null ) {
// refErr was not provided. bail out now.
return false;
}
//
// diagnose the error
//
// this attribute was not accepted.
// its value may be wrong.
// try feeding wild card and see if it's accepted.
AttributeRecoveryToken rtoken = token.createRecoveryAttToken();
r = docDecl.attFeeder.feed( this.expression, rtoken, ignoreUndeclaredAttributes );
if( r==Expression.nullSet ) {
// even the wild card was rejected.
// now there are two possibilities.
// the first is that this attribute name is not allowed to appear,
// which is the most typical case. (e.g., type miss of the attribute name, etc).
// the second is that the content model of the element is equal to the
// nullSet, thus nothing can be accepted. This is usually
// a problem of the schema.
if( this.expression==Expression.nullSet ) {
// the content model is equal to the nullSet.
refErr.str = docDecl.localizeMessage(
docDecl.DIAG_CONTENT_MODEL_IS_NULLSET, null );
} else {
// the content model is not equal to the nullSet.
// this means that this attribute
// is not specified by the grammar.
refErr.str = docDecl.localizeMessage(
docDecl.DIAG_UNDECLARED_ATTRIBUTE, token.qName );
}
// recover by using the current expression.
// TODO: possibly we can make all attributes optional or something.
// (because this might be a caused by the typo.)
return true;
} else {
// wild card was accepted, so the value must be wrong.
refErr.str = diagnoseBadAttributeValue( rtoken );
if( refErr.str==null ) {
// no detailed error message can be provided
// so use generic one.
refErr.str = docDecl.localizeMessage(
docDecl.DIAG_BAD_ATTRIBUTE_VALUE_GENERIC, token.qName );
}
// now we know the reason.
// recover by assuming that the valid value was specified for this attribute.
this.expression = r;
return true;
}
}
public boolean onEndAttributes( StartTagInfo sti, StringRef refErr ) {
Expression r = docDecl.attPruner.prune( this.expression );
if( r!=Expression.nullSet ) {
// there was no error.
this.expression = r;
return true;
}
// there was an error.
// specifically, some required attributes are missing.
if( refErr==null )
return false; // refErr was not provided. bail out.
if( this.expression==Expression.nullSet ) {
// the content model is equal to the nullSet.
refErr.str = docDecl.localizeMessage(
docDecl.DIAG_CONTENT_MODEL_IS_NULLSET, null );
} else {
refErr.str = diagnoseMissingAttribute(sti);
if( refErr.str==null )
// no detailed error message can be provided
// so use generic one.
refErr.str = docDecl.localizeMessage(
docDecl.DIAG_MISSING_ATTRIBUTE_GENERIC,
sti.qName );
}
// remove unconsumed attributes
this.expression = this.expression.visit( docDecl.attRemover );
return true;
}
protected boolean stepForward( Token token, StringRef errRef ) {
Expression residual = docDecl.resCalc.calcResidual( expression, token );
// if token is ignorable, make expression as so.
if( token.isIgnorable() ) {
residual = docDecl.pool.createChoice( residual, expression );
}
if( com.sun.msv.driver.textui.Debug.debug ) {
System.out.println("residual of stepForward("+token+")");
System.out.print(com.sun.msv.grammar.util.ExpressionPrinter.printContentModel(expression));
System.out.print(" -> ");
System.out.println(com.sun.msv.grammar.util.ExpressionPrinter.printContentModel(residual));
}
if( residual==Expression.nullSet ) {
// error: we can't accept this token
if( errRef!=null ) {
// diagnose error.
if( token instanceof StringToken )
errRef.str = diagnoseUnexpectedLiteral( (StringToken)token );
// docDecl.localizeMessage( docDecl.DIAG_BAD_LITERAL_VALUE_WRAPUP,
// TODO: diagnosis for ElementToken
// recovery by ignoring this token.
// TODO: should we modify this to choice(expression,EoCR)?
// we need some measures to prevent redundant choice
} else {
// do not mutate any member variables.
// caller may call stepForward again with error recovery.
}
return false;
}
expression = residual;
return true;
}
public boolean onText( String literal, IDContextProvider provider, StringRef refErr, DatatypeRef refType ) {
return stepForward( new StringToken(docDecl,literal,provider,refType), refErr );
}
public final boolean stepForwardByContinuation( Expression continuation, StringRef errRef ) {
if( continuation!=Expression.nullSet ) {
// successful transition
if( com.sun.msv.driver.textui.Debug.debug )
System.out.println("stepForwardByCont. : " +
com.sun.msv.grammar.util.ExpressionPrinter.printContentModel(continuation));
expression = continuation;
return true;
}
if( errRef==null ) return false; // fail immediately.
// TODO: diagnose uncompleted content model.
return false;
}
/** checks if this Acceptor is satisifed */
public boolean isAcceptState( StringRef errRef ) {
if( errRef==null )
return expression.isEpsilonReducible();
else {
if(expression.isEpsilonReducible()) return true;
// error. provide diagnosis
errRef.str = diagnoseUncompletedContent();
return false;
}
}
public int getStringCareLevel() {
// if the value is cached, return cached value.
// otherwise, calculate it now.
OptimizationTag ot = (OptimizationTag)expression.verifierTag;
if(ot==null) expression.verifierTag = ot = new OptimizationTag();
if(ot.stringCareLevel==ot.STRING_NOTCOMPUTED)
ot.stringCareLevel = StringCareLevelCalculator.calc(expression);
return ot.stringCareLevel;
}
// error recovery
//==================================================
private final Expression mergeContinuation( Expression exp1, Expression exp2 ) {
if(exp1==null && exp2==null) return null;
if(exp1==null || exp1==Expression.nullSet) return exp2;
if(exp2==null || exp2==Expression.nullSet) return exp1;
return docDecl.pool.createChoice(exp1,exp2);
}
/**
* creates Acceptor that recovers from errors.
*
* This method also modifies the current expression in preparation to
* accept newly created child acceptor.
*
* Recovery will be done by preparing to accept two possibilities.
*
* <ol>
* <li>We may get back to sync by ignoring the newly found illegal element.
* ( this is for mistake like "abcXdefg")
* <li>We may get back to sync by replacing newly found illegal element
* by one of the valid elements.
* ( this is for mistake like "abcXefg")
* </ol>
*/
private final Acceptor createRecoveryAcceptors() {
final CombinedChildContentExpCreator cccc = docDecl.cccec;
CombinedChildContentExpCreator.ExpressionPair combinedEoC =
cccc.get( expression, null, false );
// get residual of EoC.
Expression eocr = docDecl.resCalc.calcResidual( expression, AnyElementToken.theInstance );
Expression continuation = docDecl.pool.createChoice(
expression, eocr );
Expression contentModel = combinedEoC.content;
if( com.sun.msv.driver.textui.Debug.debug )
{
System.out.println("content model of recovery acceptor:"+
com.sun.msv.grammar.util.ExpressionPrinter.printContentModel(contentModel) );
System.out.println("continuation of recovery acceptor:"+
com.sun.msv.grammar.util.ExpressionPrinter.printSmallest(continuation) );
}
// by passing null as elements of concern and
// using continuation, we are effectively "generating"
// the content model for error recovery.
return createAcceptor( contentModel, continuation, null, 0 );
}
/**
* format list of candidates to one string.
*
* this method
* (1) inserts separator into appropriate positions
* (2) appends "more" message when items are only a portion of candidates.
*/
private final String concatenateMessages( List items, boolean more,
String separatorStr, String moreStr )
{
String r="";
String sep = docDecl.localizeMessage(separatorStr,null);
Collections.sort(items,
new Comparator(){
public int compare( Object o1, Object o2 ) {
return ((String)o1).compareTo((String)o2);
}
}); // sort candidates.
for( int i=0; i<items.size(); i++ ) {
if(r.length()!=0) r+= sep;
r += items.get(i);
}
if( more )
r += docDecl.localizeMessage(moreStr,null);
return r;
}
private final String concatenateMessages( Set items, boolean more,
String separatorStr, String moreStr ) {
return concatenateMessages( new Vector(items), more, separatorStr, moreStr );
}
/**
* gets error diagnosis message from datatype.
*
* @return null
* if diagnosis failed.
*/
private final String getDiagnosisFromTypedString( DataOrValueExp exp, StringToken value ) {
try {
exp.getType().checkValid( value.literal, value.context );
// TODO: diagnose errors if exp is ValueExp and
// this is a not correct value
// it should throw an exception.
// but just in case the datatype library has a bug,
// we recover from this situation
return null;
} catch( DatatypeException e ) {
return e.getMessage();
}
}
/**
* computes diagnosis message for bad tag name
*
* @param exp
* combined child content expression without
* tag name check and attributes check.
*
* @return null
* if diagnosis fails.
*/
private final String diagnoseBadTagName( StartTagInfo sti ) {
final CombinedChildContentExpCreator cccc = docDecl.cccec;
// try creating combined child content pattern without tag name check.
Expression r = cccc.get(expression,sti,false).content;
if( r==Expression.nullSet )
// no element is allowed here at all.
return docDecl.localizeMessage( docDecl.DIAG_ELEMENT_NOT_ALLOWED, sti.qName );
if( cccc.isComplex() ) {
// probably <concur> is used.
// there is no easy way to tell which what tag name is expected.
// TODO: we can reduce strength by treating concur as choice.
// do it.
return null;
}
// we are now sure that combined child content expression will be
// the choice of all elements of concern.
// so if tag name satisfies one of those elements,
// it can be accepted.
// therefore we can provide candidates for users.
Set s = new java.util.HashSet();
boolean more = false;
// if there is a SimpleNameClass with the same localName
// but with a different namespace URI,
// this variable will receive that URI.
String wrongNamespace = null;
final ElementExp[] eocs = cccc.getMatchedElements();
final int len = cccc.numMatchedElements();
for( int i=0; i<len; i++ ) {
if( eocs[i].contentModel.getExpandedExp(docDecl.pool)==Expression.nullSet )
// this element is not allowed to appear.
continue;
// test some typical name class patterns.
final NameClass nc = eocs[i].getNameClass();
if( nc instanceof SimpleNameClass ) {
SimpleNameClass snc = (SimpleNameClass)nc;
if( snc.localName.equals(sti.localName) ) {
// sometimes, people simply forget to add namespace decl,
// or declare the wrong name.
wrongNamespace = snc.namespaceURI;
}
s.add( docDecl.localizeMessage(
docDecl.DIAG_SIMPLE_NAMECLASS, nc.toString() ) );
continue;
}
if( nc instanceof NamespaceNameClass ) {
s.add( docDecl.localizeMessage(
docDecl.DIAG_NAMESPACE_NAMECLASS, ((NamespaceNameClass)nc).namespaceURI ) );
continue;
}
if( nc instanceof NotNameClass ) {
NameClass ncc = ((NotNameClass)nc).child;
if( ncc instanceof NamespaceNameClass ) {
s.add( docDecl.localizeMessage(
docDecl.DIAG_NOT_NAMESPACE_NAMECLASS, ((NamespaceNameClass)ncc).namespaceURI ) );
continue;
}
}
// this name class is very complex and
// therefore we were unable to provide appropriate suggestion.
more = true;
}
// no candidate was collected. bail out.
if( s.size()==0 ) return null;
if( wrongNamespace!=null ) {
if( s.size()==1 )
// only one candidate.
return docDecl.localizeMessage(
docDecl.DIAG_BAD_TAGNAME_WRONG_NAMESPACE, sti.localName, wrongNamespace );
else
// probably wrong namespace,
// but show the user that he/she has other choices.
return docDecl.localizeMessage(
docDecl.DIAG_BAD_TAGNAME_PROBABLY_WRONG_NAMESPACE, sti.localName, wrongNamespace );
}
// there is no clue about user's intention.
return docDecl.localizeMessage(
docDecl.DIAG_BAD_TAGNAME_WRAPUP, sti.qName,
concatenateMessages( s, more,
docDecl.DIAG_BAD_TAGNAME_SEPARATOR,
docDecl.DIAG_BAD_TAGNAME_MORE ) );
}
/**
* computes diagnosis message for bad attribute value
*
* @param rtoken
* wild card AttributeToken that was used.
* @param attIndex
* index in sti.attributes of the attribute
*
* @return null
* if diagnosis fails.
*/
private final String diagnoseBadAttributeValue( AttributeRecoveryToken rtoken ) {
// if the combined child content expression is not complex,
// only binary expressions used are choice and sequence.
// this is the choice of all constraints that made this
// attribute fail.
Expression constraint = rtoken.getFailedExp();
// The problem here is that sti.attributes.getValue(i)
// didn't satisfy this expression.
// test some typical expression patterns and
// provide error messages if it matchs the pattern.
// otherwise provide a generic error message.
if( constraint instanceof DataOrValueExp ) {
// if only one AttributeExp is specified for this attribute
// and if it has a TypedString as its child.
// for RELAX, this is the only possible case
DataOrValueExp tse = (DataOrValueExp)constraint;
if( tse.getType() == com.sun.msv.grammar.relax.NoneType.theInstance ) {
// if the underlying datatype is "none",
// this should be reported as unexpected attribute.
return docDecl.localizeMessage(
docDecl.DIAG_UNDECLARED_ATTRIBUTE,
rtoken.qName );
}
String dtMsg = getDiagnosisFromTypedString( tse, rtoken.value );
if(dtMsg==null) return null;
return docDecl.localizeMessage(
docDecl.DIAG_BAD_ATTRIBUTE_VALUE_DATATYPE,
rtoken.qName, dtMsg );
}
if( constraint instanceof ChoiceExp ) {
// choice of <string>s.
//
// this is also a frequently used pattern by TREX.
// an expression like
//
// <attribute name="export">
// <choice>
// <string>yes</string><string>no</string>
// </choice>
// </attribute>
//
// falls into this pattern.
final Set items = new java.util.HashSet();
boolean more = false;
ChoiceExp ch = (ChoiceExp)constraint;
Expression[] children = ch.getChildren();
for( int i=0; i<children.length; i++ ) {
if( children[i] instanceof ValueExp )
items.add( ((ValueExp)children[i]).value.toString() );
else
// this is a fairly complex expression
// that we can't provide diagnosis.
more = true;
}
// no candidates was simple. bail out.
if( items.size()==0 ) return null;
// at least we have one suggestion.
return docDecl.localizeMessage(
docDecl.DIAG_BAD_ATTRIBUTE_VALUE_WRAPUP,
rtoken.qName,
concatenateMessages( items, more,
docDecl.DIAG_BAD_ATTRIBUTE_VALUE_SEPARATOR,
docDecl.DIAG_BAD_ATTRIBUTE_VALUE_MORE ) );
}
return null; // this constraint didn't fall into known patterns.
}
/**
* computes diagnosis message for missing attribute
*
* @return null
* if diagnosis fails.
*/
private final String diagnoseMissingAttribute( StartTagInfo sti ) {
// if( cccc.isComplex() )
// // again if the expression is complex,
// // hope is remote that we can find required attributes.
//
// // TODO: reduce strength by converting concur to choice?
// return null;
Expression e = expression.visit(docDecl.attPicker);
if( e.isEpsilonReducible() ) throw new Error(); // assertion
// if attribute expression is epsilon reducible, then
// AttributePruner must return Expression other than nullSet.
// In that case, there should have been no error.
final Set s = new java.util.HashSet();
boolean more = false;
while( e instanceof ChoiceExp ) {
ChoiceExp ch = (ChoiceExp)e;
NameClass nc = ((AttributeExp)ch.exp2).nameClass;
if( nc instanceof SimpleNameClass )
s.add( nc.toString() );
else
more = true;
e = ch.exp1;
}
if( e==Expression.nullSet )
// we are in the full panic mode.
// abandon diagnosis.
return null;
if(!(e instanceof AttributeExp )) throw new Error(e.toString()); //assertion
NameClass nc = ((AttributeExp)e).nameClass;
if( nc instanceof SimpleNameClass )
s.add( nc.toString() );
else
more = true;
if( s.size()==0 ) return null;
// at least one candidate is found
if( s.size()==1 && !more )
{// only one candidate
return docDecl.localizeMessage(
docDecl.DIAG_MISSING_ATTRIBUTE_SIMPLE,
sti.qName,s.iterator().next() );
}
else
// list candidates
return docDecl.localizeMessage(
docDecl.DIAG_MISSING_ATTRIBUTE_WRAPUP,
sti.qName,
concatenateMessages( s, more,
docDecl.DIAG_MISSING_ATTRIBUTE_SEPARATOR,
docDecl.DIAG_MISSING_ATTRIBUTE_MORE ) );
}
/**
* diagnoses an error when a StringToken is rejected.
*/
private final String diagnoseUnexpectedLiteral( StringToken token ) {
final StringRecoveryToken srt = new StringRecoveryToken(token);
// this residual corresponds to the expression we get
// when we replace thie unexpected token by one of expected tokens.
Expression recoveryResidual
= docDecl.resCalc.calcResidual(expression,srt);
if( recoveryResidual==Expression.nullSet )
// we now know that no string literal was expected at all.
return docDecl.localizeMessage( docDecl.DIAG_STRING_NOT_ALLOWED, null );
// keep this.expression untouched. This is equivalent to ignore this token.
// there are two possible "recovery" for this error.
// (1) ignore this token
// (2) replace this token by a valid token.
// the following choice implements both of them.
expression = docDecl.pool.createChoice( expression, recoveryResidual );
if( srt.failedExps.size()==1 ) {
DataOrValueExp texp = (DataOrValueExp)srt.failedExps.iterator().next();
try {
// TODO: handle ValueExp nicely
texp.getType().checkValid( srt.literal, srt.context );
if(texp instanceof ValueExp) {
ValueExp vexp = (ValueExp)texp;
if(!vexp.dt.sameValue(vexp.value,
vexp.dt.createValue(srt.literal,srt.context))) {
// incorrect value
return docDecl.localizeMessage(
docDecl.DIAG_BAD_LITERAL_INCORRECT_VALUE,
vexp.value.toString() );
}
}
} catch( DatatypeException de ) {
// this literal is invalid.
if( de.getMessage()!=null )
return de.getMessage(); // return the diagnosis.
// we don't know the exact reason, but the value was wrong.
return docDecl.localizeMessage( docDecl.DIAG_BAD_LITERAL_GENERIC, null );
}
} else {
// there are multiple candidates.
final Set items = new java.util.HashSet();
boolean more = false;
Iterator itr = srt.failedExps.iterator();
while(itr.hasNext()) {
DataOrValueExp texp = (DataOrValueExp)itr.next();
if( texp instanceof ValueExp )
// we can list this item as one of the candidates
items.add( ((ValueExp)texp).value.toString() );
else
// this must be some datatype
// that we can't provide diagnosis.
more = true;
}
// no candidates was simple. bail out.
if( items.size()==0 ) return null;
// at least we have one suggestion.
return docDecl.localizeMessage(
docDecl.DIAG_BAD_LITERAL_WRAPUP,
concatenateMessages( items, more,
docDecl.DIAG_BAD_LITERAL_SEPARATOR,
docDecl.DIAG_BAD_LITERAL_MORE ) );
}
// unable to diagnose the reason of error.
return null;
// TODO: ID/IDREF violation diagnosis.
/*
// now the literal is valid.
// Is this key/keyref constraint violation?
if( texp instanceof NGTypedStringExp ) {
NGTypedStringExp ntexp = (NGTypedStringExp)texp;
if( ntexp.keyName!=null
&& !token.context.onID( ntexp.keyName.namespaceURI, ntexp.keyName.localName, ntexp.dt.createValue(token.literal,token.context) ) ) {
if( ntexp.keyName.localName.length()==0 )
// empty key name indicates that this is an ID.
return docDecl.localizeMessage( docDecl.DIAG_BAD_KEY_VALUE,
token.literal.trim() );
else
return docDecl.localizeMessage( docDecl.DIAG_BAD_KEY_VALUE2,
token.literal.trim(), ntexp.keyName );
}
}
*/
}
/**
* diagnoses "uncompleted content model" error.
* It basically provides what we were expected.
*/
protected String diagnoseUncompletedContent() {
final CombinedChildContentExpCreator cccc = docDecl.cccec;
cccc.get( expression, null, false );
Set s = new java.util.HashSet(); // this set will receive possible tag names.
boolean more = false; // this flag is set to true if there are more
// candidate.
final ElementExp[] eocs = cccc.getMatchedElements();
final int len = cccc.numMatchedElements();
for( int i=0; i<len; i++ ) {
// test some typical name class patterns.
final NameClass nc = eocs[i].getNameClass();
if( nc instanceof SimpleNameClass ) {
s.add( docDecl.localizeMessage(
docDecl.DIAG_SIMPLE_NAMECLASS, nc.toString() ) );
continue;
}
if( nc instanceof NamespaceNameClass ) {
s.add( docDecl.localizeMessage(
docDecl.DIAG_NAMESPACE_NAMECLASS,
((NamespaceNameClass)nc).namespaceURI ) );
continue;
}
if( nc instanceof NotNameClass ) {
NameClass ncc = ((NotNameClass)nc).child;
if( ncc instanceof NamespaceNameClass ) {
s.add( docDecl.localizeMessage(
docDecl.DIAG_NOT_NAMESPACE_NAMECLASS, ((NamespaceNameClass)ncc).namespaceURI ) );
continue;
}
}
// this name class is very complex and
// therefore we were unable to provide appropriate suggestion.
more = true;
}
// no candidate was collected. bail out.
// this happens when we are expecting a string.
if( s.size()==0 ) return null;
return docDecl.localizeMessage(
docDecl.DIAG_UNCOMPLETED_CONTENT_WRAPUP, null,
concatenateMessages( s, more,
docDecl.DIAG_UNCOMPLETED_CONTENT_SEPARATOR,
docDecl.DIAG_UNCOMPLETED_CONTENT_MORE ) );
}
}