0001: package net.sf.saxon.query;
0002:
0003: import net.sf.saxon.Configuration;
0004: import net.sf.saxon.Err;
0005: import net.sf.saxon.StandardURIResolver;
0006: import net.sf.saxon.event.PipelineConfiguration;
0007: import net.sf.saxon.expr.*;
0008: import net.sf.saxon.functions.*;
0009: import net.sf.saxon.instruct.*;
0010: import net.sf.saxon.om.*;
0011: import net.sf.saxon.pattern.NodeTest;
0012: import net.sf.saxon.sort.FixedSortKeyDefinition;
0013: import net.sf.saxon.sort.IntHashSet;
0014: import net.sf.saxon.sort.TupleExpression;
0015: import net.sf.saxon.sort.TupleSorter;
0016: import net.sf.saxon.style.AttributeValueTemplate;
0017: import net.sf.saxon.style.StandardNames;
0018: import net.sf.saxon.trace.Location;
0019: import net.sf.saxon.trans.IndependentContext;
0020: import net.sf.saxon.trans.StaticError;
0021: import net.sf.saxon.trans.XPathException;
0022: import net.sf.saxon.trans.DynamicError;
0023: import net.sf.saxon.type.ItemType;
0024: import net.sf.saxon.type.SchemaType;
0025: import net.sf.saxon.type.Type;
0026: import net.sf.saxon.type.TypeHierarchy;
0027: import net.sf.saxon.value.*;
0028:
0029: import javax.xml.transform.OutputKeys;
0030: import javax.xml.transform.TransformerConfigurationException;
0031: import javax.xml.transform.stream.StreamSource;
0032: import java.net.URI;
0033: import java.net.URISyntaxException;
0034: import java.util.*;
0035:
0036: /**
0037: * This class defines extensions to the XPath parser to handle the additional
0038: * syntax supported in XQuery
0039: */
0040: class QueryParser extends ExpressionParser {
0041:
0042: private boolean preserveSpace = false;
0043: private boolean defaultEmptyLeast = true;
0044:
0045: private int errorCount = 0;
0046: private StaticError firstError = null;
0047:
0048: protected Executable executable;
0049:
0050: private boolean foundCopyNamespaces = false;
0051: private boolean foundBoundarySpaceDeclaration = false;
0052: private boolean foundOrderingDeclaration = false;
0053: private boolean foundEmptyOrderingDeclaration = false;
0054: private boolean foundDefaultCollation = false;
0055: private boolean foundConstructionDeclaration = false;
0056: private boolean foundDefaultFunctionNamespace = false;
0057: private boolean foundDefaultElementNamespace = false;
0058: private boolean foundBaseURIDeclaration = false;
0059: private boolean preambleProcessed = false;
0060:
0061: public Set importedModules = new HashSet(5);
0062: List namespacesToBeSealed = new ArrayList(10);
0063: List schemaImports = new ArrayList(5);
0064: List moduleImports = new ArrayList(5);
0065:
0066: private Expression defaultValue = null;
0067:
0068: /**
0069: * Protected Constructor: this class should be instantiated via the StaticQueryContext
0070: */
0071:
0072: protected QueryParser() {
0073: };
0074:
0075: /**
0076: * Create an XQueryExpression
0077: */
0078:
0079: public XQueryExpression makeXQueryExpression(String query,
0080: StaticQueryContext staticContext, Configuration config)
0081: throws XPathException {
0082: try {
0083: if (config.getXMLVersion() == Configuration.XML10) {
0084: query = normalizeLineEndings10(query);
0085: } else {
0086: query = normalizeLineEndings11(query);
0087: }
0088: Executable exec = new Executable();
0089:
0090: Properties outputProps = new Properties();
0091: outputProps.setProperty(OutputKeys.METHOD, "xml");
0092: outputProps.setProperty(OutputKeys.INDENT, "yes");
0093: exec.setDefaultOutputProperties(outputProps);
0094:
0095: exec.setLocationMap(new LocationMap());
0096: exec.setConfiguration(config);
0097: exec.setFunctionLibrary(new ExecutableFunctionLibrary(
0098: config));
0099: // this will be changed later
0100: exec.setHostLanguage(Configuration.XQUERY);
0101: setExecutable(exec);
0102: staticContext.setExecutable(exec);
0103: Expression exp = parseQuery(query, 0, Token.EOF,
0104: staticContext);
0105: if (exp instanceof ComputedExpression) {
0106: int loc = env.getLocationMap().allocateLocationId(
0107: env.getSystemId(), 1);
0108: ((ComputedExpression) exp)
0109: .setParentExpression(new TemporaryContainer(
0110: staticContext.getLocationMap(), loc));
0111: }
0112: staticContext.bindUnboundFunctionCalls();
0113: exec.fixupQueryModules(staticContext);
0114: XQueryExpression queryExp = new XQueryExpression(exp, exec,
0115: staticContext, config);
0116: exp = queryExp.getExpression();
0117: DocumentInstr docInstruction;
0118: if (exp instanceof DocumentInstr) {
0119: docInstruction = (DocumentInstr) exp;
0120: } else {
0121: docInstruction = new DocumentInstr(false, null,
0122: staticContext.getSystemId());
0123: docInstruction.setContentExpression(exp);
0124: setLocation(docInstruction, 1);
0125: //makeContentConstructor(exp, docInstruction, 1);
0126: }
0127: queryExp.setDocumentInstruction(docInstruction);
0128:
0129: // Make the function library that's available at run-time (e.g. for saxon:evaluate()). This includes
0130: // all user-defined functions regardless of which module they are in
0131:
0132: FunctionLibrary userlib = exec.getFunctionLibrary();
0133: FunctionLibraryList lib = new FunctionLibraryList();
0134: lib.addFunctionLibrary(new SystemFunctionLibrary(
0135: SystemFunctionLibrary.XPATH_ONLY));
0136: lib.addFunctionLibrary(config.getVendorFunctionLibrary());
0137: lib.addFunctionLibrary(new ConstructorFunctionLibrary(
0138: config));
0139: if (config.isAllowExternalFunctions()) {
0140: lib.addFunctionLibrary(config.getExtensionBinder());
0141: }
0142: lib.addFunctionLibrary(userlib);
0143: exec.setFunctionLibrary(lib);
0144:
0145: return queryExp;
0146: } catch (XPathException e) {
0147: if (!e.hasBeenReported()) {
0148: e = StaticError.makeStaticError(e);
0149: reportError((StaticError) e);
0150: }
0151: throw e;
0152: }
0153: }
0154:
0155: /**
0156: * Normalize line endings in the source query, according to the XML 1.1 rules.
0157: */
0158:
0159: private static String normalizeLineEndings11(String in) {
0160: if (in.indexOf((char) 0xa) < 0 && in.indexOf((char) 0x85) < 0
0161: && in.indexOf((char) 0x2028) < 0) {
0162: return in;
0163: }
0164: FastStringBuffer sb = new FastStringBuffer(in.length());
0165: for (int i = 0; i < in.length(); i++) {
0166: char ch = in.charAt(i);
0167: switch (ch) {
0168: case 0x85:
0169: case 0x2028:
0170: sb.append((char) 0xa);
0171: break;
0172: case 0xd:
0173: if (i < in.length() - 1
0174: && (in.charAt(i + 1) == (char) 0xa || in
0175: .charAt(i + 1) == (char) 0x85)) {
0176: sb.append((char) 0xa);
0177: i++;
0178: } else {
0179: sb.append((char) 0xa);
0180: }
0181: break;
0182: default:
0183: sb.append(ch);
0184: }
0185: }
0186: return sb.toString();
0187: }
0188:
0189: /**
0190: * Normalize line endings in the source query, according to the XML 1.0 rules.
0191: */
0192:
0193: private static String normalizeLineEndings10(String in) {
0194: if (in.indexOf((char) 0xa) < 0) {
0195: return in;
0196: }
0197: FastStringBuffer sb = new FastStringBuffer(in.length());
0198: for (int i = 0; i < in.length(); i++) {
0199: char ch = in.charAt(i);
0200: switch (ch) {
0201: case 0xd:
0202: if (i < in.length() - 1
0203: && in.charAt(i + 1) == (char) 0xa) {
0204: sb.append((char) 0xa);
0205: i++;
0206: } else {
0207: sb.append((char) 0xa);
0208: }
0209: break;
0210: default:
0211: sb.append(ch);
0212: }
0213: }
0214: return sb.toString();
0215: }
0216:
0217: /**
0218: * Get the executable containing this expression.
0219: */
0220:
0221: public Executable getExecutable() {
0222: return executable;
0223: }
0224:
0225: /**
0226: * Set the executable used for this query expression
0227: */
0228:
0229: public void setExecutable(Executable exec) {
0230: executable = exec;
0231: }
0232:
0233: /**
0234: * Parse a top-level Query.
0235: * Prolog? Expression
0236: *
0237: * @param queryString The text of the query
0238: * @param start Offset of the start of the query
0239: * @param terminator Token expected to follow the query (usually Token.EOF)
0240: * @param env The static context
0241: * @return the Expression object that results from parsing
0242: * @throws net.sf.saxon.trans.XPathException
0243: * if the expression contains a syntax error
0244: */
0245:
0246: private Expression parseQuery(String queryString, int start,
0247: int terminator, StaticQueryContext env)
0248: throws XPathException {
0249: this .env = env;
0250: this .nameChecker = env.getConfiguration().getNameChecker();
0251: this .language = XQUERY;
0252: t = new Tokenizer();
0253: try {
0254: t.tokenize(queryString, start, -1, 1);
0255: } catch (StaticError err) {
0256: grumble(err.getMessage());
0257: }
0258: parseVersionDeclaration();
0259: parseProlog();
0260: processPreamble();
0261: Expression exp = parseExpression();
0262: if (t.currentToken != terminator) {
0263: grumble("Unexpected token " + currentTokenDisplay()
0264: + " beyond end of query");
0265: }
0266: setLocation(exp);
0267: if (errorCount == 0) {
0268: return exp;
0269: } else {
0270: StaticError err = new StaticError(
0271: "One or more static errors were reported during query analysis");
0272: err.setHasBeenReported();
0273: err.setErrorCode(firstError.getErrorCodeLocalPart()); // largely for the XQTS test driver
0274: throw err;
0275: }
0276: }
0277:
0278: /**
0279: * Parse a library module.
0280: * Prolog? Expression
0281: *
0282: * @param queryString The text of the library module.
0283: * @param env The static context. The result of parsing
0284: * a library module is that the static context is populated with a set of function
0285: * declarations and variable declarations. Each library module must have its own
0286: * static context objext.
0287: * @throws net.sf.saxon.trans.StaticError if the expression contains a syntax error
0288: */
0289:
0290: public final void parseLibraryModule(String queryString,
0291: StaticQueryContext env) throws StaticError {
0292: this .env = env;
0293: this .nameChecker = env.getConfiguration().getNameChecker();
0294: this .executable = env.getExecutable();
0295: t = new Tokenizer();
0296: try {
0297: t.tokenize(queryString, 0, -1, 1);
0298: } catch (StaticError err) {
0299: grumble(err.getMessage());
0300: }
0301: parseVersionDeclaration();
0302: parseModuleDeclaration();
0303: parseProlog();
0304: processPreamble();
0305: if (t.currentToken != Token.EOF) {
0306: grumble("Unrecognized content found after the variable and function declarations in a library module");
0307: }
0308: if (errorCount != 0) {
0309: throw new StaticError(
0310: "Static errors were reported in the imported library module");
0311: }
0312: }
0313:
0314: /**
0315: * Report a static error
0316: *
0317: * @param message the error message
0318: * @throws net.sf.saxon.trans.StaticError always thrown: an exception containing the
0319: * supplied message
0320: */
0321:
0322: protected void grumble(String message, String errorCode)
0323: throws StaticError {
0324: String s = t.recentText();
0325: ExpressionLocation loc = makeLocator();
0326: String prefix = getLanguage()
0327: + ("XPST0003".equals(errorCode) ? " syntax error "
0328: : " static error ")
0329: + (message.startsWith("...") ? "near" : "in") + " #"
0330: + s + "#:\n ";
0331: StaticError exception = new StaticError(prefix + message);
0332: exception.setErrorCode(errorCode);
0333: exception.setLocator(loc);
0334: reportError(exception);
0335: }
0336:
0337: private void reportError(StaticError exception) throws StaticError {
0338: errorCount++;
0339: if (firstError == null) {
0340: firstError = exception;
0341: }
0342: env.getConfiguration().reportFatalError(exception);
0343: throw exception;
0344: }
0345:
0346: /**
0347: * Make a Locator object representing the current parsing location
0348: *
0349: * @return a Locator
0350: */
0351: private ExpressionLocation makeLocator() {
0352: int line = t.getLineNumber();
0353: int column = t.getColumnNumber();
0354:
0355: ExpressionLocation loc = new ExpressionLocation();
0356: loc.setSystemId(env.getSystemId());
0357: loc.setLineNumber(line);
0358: loc.setColumnNumber(column);
0359: return loc;
0360: }
0361:
0362: /**
0363: * Parse the version declaration if present.
0364: *
0365: * @throws net.sf.saxon.trans.StaticError in the event of a syntax error.
0366: */
0367: private void parseVersionDeclaration() throws StaticError {
0368: if (t.currentToken == Token.XQUERY_VERSION) {
0369: nextToken();
0370: expect(Token.STRING_LITERAL);
0371: if (!("1.0".equals(t.currentTokenValue))) {
0372: grumble("XQuery version must be 1.0", "XQST0031");
0373: }
0374: nextToken();
0375: if ("encoding".equals(t.currentTokenValue)) {
0376: nextToken();
0377: expect(Token.STRING_LITERAL);
0378: // we ignore the encoding now: it was handled earlier, while decoding the byte stream
0379: nextToken();
0380: }
0381: expect(Token.SEMICOLON);
0382: nextToken();
0383: }
0384: }
0385:
0386: /**
0387: * In a library module, parse the module declaration
0388: * Syntax: <"module" "namespace"> prefix "=" uri ";"
0389: *
0390: * @throws net.sf.saxon.trans.StaticError in the event of a syntax error.
0391: */
0392:
0393: private void parseModuleDeclaration() throws StaticError {
0394: expect(Token.MODULE_NAMESPACE);
0395: nextToken();
0396: expect(Token.NAME);
0397: String prefix = t.currentTokenValue;
0398: checkProhibitedPrefixes(prefix);
0399: nextToken();
0400: expect(Token.EQUALS);
0401: nextToken();
0402: expect(Token.STRING_LITERAL);
0403: String uri = URILiteral(t.currentTokenValue);
0404: if (uri.equals("")) {
0405: grumble("Module namespace cannot be \"\"", "XQST0008");
0406: uri = "http://saxon.fallback.namespace/"; // for error recovery
0407: }
0408: nextToken();
0409: expect(Token.SEMICOLON);
0410: nextToken();
0411: try {
0412: ((StaticQueryContext) env).declarePassiveNamespace(prefix,
0413: uri, true);
0414: } catch (StaticError err) {
0415: err.setLocator(makeLocator());
0416: reportError(err);
0417: }
0418: ((StaticQueryContext) env).setModuleNamespace(uri);
0419: }
0420:
0421: /**
0422: * Parse the query prolog. This method, and its subordinate methods which handle
0423: * individual declarations in the prolog, cause the static context to be updated
0424: * with relevant context information. On exit, t.currentToken is the first token
0425: * that is not recognized as being part of the prolog.
0426: *
0427: * @throws net.sf.saxon.trans.StaticError in the event of a syntax error.
0428: */
0429:
0430: private void parseProlog() throws StaticError {
0431: //boolean allowSetters = true;
0432: boolean allowModuleDecl = true;
0433: boolean allowDeclarations = true;
0434:
0435: while (true) {
0436: try {
0437: if (t.currentToken == Token.MODULE_NAMESPACE) {
0438: String uri = ((StaticQueryContext) env)
0439: .getModuleNamespace();
0440: if (uri == null) {
0441: grumble("Module declaration must not be used in a main module");
0442: } else {
0443: grumble("Module declaration appears more than once");
0444: }
0445: if (!allowModuleDecl) {
0446: grumble("Module declaration must precede other declarations in the query prolog");
0447: }
0448: }
0449: allowModuleDecl = false;
0450: switch (t.currentToken) {
0451: case Token.DECLARE_NAMESPACE:
0452: if (!allowDeclarations) {
0453: grumble("Namespace declarations cannot follow variables, functions, or options");
0454: }
0455: //allowSetters = false;
0456: parseNamespaceDeclaration();
0457: break;
0458: case Token.DECLARE_DEFAULT:
0459: nextToken();
0460: expect(Token.NAME);
0461: if (t.currentTokenValue == "element") {
0462: if (!allowDeclarations) {
0463: grumble("Namespace declarations cannot follow variables, functions, or options");
0464: }
0465: //allowSetters = false;
0466: parseDefaultElementNamespace();
0467: } else if (t.currentTokenValue == "function") {
0468: if (!allowDeclarations) {
0469: grumble("Namespace declarations cannot follow variables, functions, or options");
0470: }
0471: //allowSetters = false;
0472: parseDefaultFunctionNamespace();
0473: } else if (t.currentTokenValue == "collation") {
0474: if (!allowDeclarations) {
0475: grumble("Collation declarations must appear earlier in the prolog");
0476: }
0477: parseDefaultCollation();
0478: } else if (t.currentTokenValue == "order") {
0479: if (!allowDeclarations) {
0480: grumble("Order declarations must appear earlier in the prolog");
0481: }
0482: parseDefaultOrder();
0483: } else {
0484: grumble("After 'declare default', expected 'element', 'function', or 'collation'");
0485: }
0486: break;
0487: case Token.DECLARE_BOUNDARY_SPACE:
0488: if (!allowDeclarations) {
0489: grumble("'declare boundary-space' must appear earlier in the query prolog");
0490: }
0491: parseBoundarySpaceDeclaration();
0492: break;
0493: case Token.DECLARE_ORDERING:
0494: if (!allowDeclarations) {
0495: grumble("'declare ordering' must appear earlier in the query prolog");
0496: }
0497: parseOrderingDeclaration();
0498: break;
0499: case Token.DECLARE_COPY_NAMESPACES:
0500: if (!allowDeclarations) {
0501: grumble("'declare copy-namespaces' must appear earlier in the query prolog");
0502: }
0503: parseCopyNamespacesDeclaration();
0504: break;
0505: case Token.DECLARE_BASEURI:
0506: if (!allowDeclarations) {
0507: grumble("'declare base-uri' must appear earlier in the query prolog");
0508: }
0509: parseBaseURIDeclaration();
0510: break;
0511: case Token.IMPORT_SCHEMA:
0512: //allowSetters = false;
0513: if (!allowDeclarations) {
0514: grumble("Import schema must appear earlier in the prolog");
0515: }
0516: parseSchemaImport();
0517: break;
0518: case Token.IMPORT_MODULE:
0519: //allowSetters = false;
0520: if (!allowDeclarations) {
0521: grumble("Import module must appear earlier in the prolog");
0522: }
0523: parseModuleImport();
0524: break;
0525: case Token.DECLARE_VARIABLE:
0526: //allowSetters = false;
0527: if (allowDeclarations) {
0528: sealNamespaces(namespacesToBeSealed, env
0529: .getConfiguration());
0530: allowDeclarations = false;
0531: }
0532: processPreamble();
0533: parseVariableDeclaration();
0534: break;
0535: case Token.DECLARE_FUNCTION:
0536: //allowSetters = false;
0537: if (allowDeclarations) {
0538: sealNamespaces(namespacesToBeSealed, env
0539: .getConfiguration());
0540: allowDeclarations = false;
0541: }
0542: processPreamble();
0543: parseFunctionDeclaration();
0544: break;
0545: case Token.DECLARE_OPTION:
0546: //allowSetters = false;
0547: if (allowDeclarations) {
0548: sealNamespaces(namespacesToBeSealed, env
0549: .getConfiguration());
0550: allowDeclarations = false;
0551: }
0552: processPreamble();
0553: parseOptionDeclaration();
0554: break;
0555: case Token.DECLARE_CONSTRUCTION:
0556: if (!allowDeclarations) {
0557: grumble("'declare construction' must appear earlier in the query prolog");
0558: }
0559: parseConstructionDeclaration();
0560: break;
0561: default:
0562: return;
0563: }
0564: expect(Token.SEMICOLON);
0565: nextToken();
0566: } catch (StaticError err) {
0567: if (err.getLocator() == null) {
0568: err.setLocator(makeLocator());
0569: }
0570: if (!err.hasBeenReported()) {
0571: errorCount++;
0572: if (firstError == null) {
0573: firstError = err;
0574: }
0575: env.getConfiguration().reportFatalError(err);
0576: }
0577: // we've reported an error, attempt to recover by skipping to the
0578: // next semicolon
0579: while (t.currentToken != Token.SEMICOLON) {
0580: nextToken();
0581: if (t.currentToken == Token.EOF) {
0582: return;
0583: } else if (t.currentToken == Token.RCURLY) {
0584: t.lookAhead();
0585: } else if (t.currentToken == Token.TAG) {
0586: parsePseudoXML(true);
0587: }
0588: }
0589: nextToken();
0590: }
0591: }
0592: }
0593:
0594: private void sealNamespaces(List namespacesToBeSealed,
0595: Configuration config) {
0596: for (Iterator iter = namespacesToBeSealed.iterator(); iter
0597: .hasNext();) {
0598: String ns = (String) iter.next();
0599: config.sealNamespace(ns);
0600: }
0601: }
0602:
0603: /**
0604: * Method called once the setters have been read to do tidying up that can't be done until we've got
0605: * to the end
0606: *
0607: * @throws StaticError
0608: */
0609:
0610: private void processPreamble() throws StaticError {
0611: if (preambleProcessed) {
0612: return;
0613: }
0614: preambleProcessed = true;
0615: if (foundDefaultCollation) {
0616: String collationName = env.getDefaultCollationName();
0617: URI collationURI;
0618: try {
0619: collationURI = new URI(collationName);
0620: if (!collationURI.isAbsolute()) {
0621: URI base = new URI(env.getBaseURI());
0622: collationURI = base.resolve(collationURI);
0623: collationName = collationURI.toString();
0624: }
0625: } catch (URISyntaxException err) {
0626: grumble("Default collation name '" + collationName
0627: + "' is not a valid URI");
0628: collationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
0629: }
0630: if (env.getCollation(collationName) == null) {
0631: grumble("Default collation name '" + collationName
0632: + "' is not a recognized collation", "XQST0038");
0633: collationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
0634: }
0635: ((StaticQueryContext) env)
0636: .declareDefaultCollation(collationName);
0637: }
0638: for (Iterator iter = schemaImports.iterator(); iter.hasNext();) {
0639: Import imp = (Import) iter.next();
0640: applySchemaImport(imp);
0641: }
0642: for (Iterator iter = moduleImports.iterator(); iter.hasNext();) {
0643: Import imp = (Import) iter.next();
0644: applyModuleImport(imp);
0645: }
0646: }
0647:
0648: private void parseDefaultCollation() throws StaticError {
0649: // <"default" "collation"> StringLiteral
0650: if (foundDefaultCollation) {
0651: grumble("default collation appears more than once",
0652: "XQST0038");
0653: }
0654: foundDefaultCollation = true;
0655: nextToken();
0656: expect(Token.STRING_LITERAL);
0657: String uri = URILiteral(t.currentTokenValue);
0658: ((StaticQueryContext) env).declareDefaultCollation(uri);
0659: nextToken();
0660: }
0661:
0662: /**
0663: * parse "declare default order empty (least|greatest)"
0664: */
0665: private void parseDefaultOrder() throws StaticError {
0666: if (foundEmptyOrderingDeclaration) {
0667: grumble(
0668: "empty ordering declaration appears more than once",
0669: "XQST0069");
0670: }
0671: foundEmptyOrderingDeclaration = true;
0672: nextToken();
0673: if (!isKeyword("empty")) {
0674: grumble("After 'declare default order', expected keyword 'empty'");
0675: }
0676: nextToken();
0677: if (isKeyword("least")) {
0678: defaultEmptyLeast = true;
0679: } else if (isKeyword("greatest")) {
0680: defaultEmptyLeast = false;
0681: } else {
0682: grumble("After 'declare default order empty', expected keyword 'least' or 'greatest'");
0683: }
0684: nextToken();
0685: }
0686:
0687: /**
0688: * Parse the "declare xmlspace" declaration.
0689: * Syntax: <"declare" "boundary-space"> ("preserve" | "strip")
0690: *
0691: * @throws net.sf.saxon.trans.StaticError
0692: */
0693:
0694: private void parseBoundarySpaceDeclaration() throws StaticError {
0695: if (foundBoundarySpaceDeclaration) {
0696: grumble("'declare boundary-space' appears more than once",
0697: "XQST0068");
0698: }
0699: foundBoundarySpaceDeclaration = true;
0700: nextToken();
0701: expect(Token.NAME);
0702: if ("preserve".equals(t.currentTokenValue)) {
0703: preserveSpace = true;
0704: } else if ("strip".equals(t.currentTokenValue)) {
0705: preserveSpace = false;
0706: } else {
0707: grumble("boundary-space must be 'preserve' or 'strip'");
0708: }
0709: nextToken();
0710: }
0711:
0712: /**
0713: * Parse the "declare ordering" declaration.
0714: * Syntax: <"declare" "ordering"> ("ordered" | "unordered")
0715: *
0716: * @throws net.sf.saxon.trans.StaticError
0717: */
0718:
0719: private void parseOrderingDeclaration() throws StaticError {
0720: if (foundOrderingDeclaration) {
0721: grumble("ordering mode declaration appears more than once",
0722: "XQST0065");
0723: }
0724: foundOrderingDeclaration = true;
0725: nextToken();
0726: expect(Token.NAME);
0727: if ("ordered".equals(t.currentTokenValue)) {
0728: // no action
0729: } else if ("unordered".equals(t.currentTokenValue)) {
0730: // no action
0731: } else {
0732: grumble("ordering mode must be 'ordered' or 'unordered'");
0733: }
0734: nextToken();
0735: }
0736:
0737: /**
0738: * Parse the "declare copy-namespaces" declaration.
0739: * Syntax: <"declare" "copy-namespaces"> ("preserve" | "no-preserve") "," ("inherit" | "no-inherit")
0740: *
0741: * @throws net.sf.saxon.trans.StaticError
0742: */
0743:
0744: private void parseCopyNamespacesDeclaration() throws StaticError {
0745: if (foundCopyNamespaces) {
0746: grumble(
0747: "declare inherit-namespaces appears more than once",
0748: "XQST0055");
0749: }
0750: foundCopyNamespaces = true;
0751: nextToken();
0752: expect(Token.NAME);
0753: if ("preserve".equals(t.currentTokenValue)) {
0754: ((StaticQueryContext) env).setPreserveNamespaces(true);
0755: } else if ("no-preserve".equals(t.currentTokenValue)) {
0756: ((StaticQueryContext) env).setPreserveNamespaces(false);
0757: } else {
0758: grumble("copy-namespaces must be followed by 'preserve' or 'no-preserve'");
0759: }
0760: nextToken();
0761: expect(Token.COMMA);
0762: nextToken();
0763: expect(Token.NAME);
0764: if ("inherit".equals(t.currentTokenValue)) {
0765: ((StaticQueryContext) env).setInheritNamespaces(true);
0766: } else if ("no-inherit".equals(t.currentTokenValue)) {
0767: ((StaticQueryContext) env).setInheritNamespaces(false);
0768: } else {
0769: grumble("After the comma in the copy-namespaces declaration, expected 'inherit' or 'no-inherit'");
0770: }
0771: nextToken();
0772: }
0773:
0774: /**
0775: * Parse the "declare construction" declaration.
0776: * Syntax: <"declare" "construction"> ("preserve" | "strip")
0777: *
0778: * @throws net.sf.saxon.trans.StaticError
0779: */
0780:
0781: private void parseConstructionDeclaration() throws StaticError {
0782: if (foundConstructionDeclaration) {
0783: grumble("declare construction appears more than once",
0784: "XQST0067");
0785: }
0786: foundConstructionDeclaration = true;
0787: nextToken();
0788: expect(Token.NAME);
0789: int val;
0790: if ("preserve".equals(t.currentTokenValue)) {
0791: val = Validation.PRESERVE;
0792: } else if ("strip".equals(t.currentTokenValue)) {
0793: val = Validation.STRIP;
0794: } else {
0795: grumble("construction mode must be 'preserve' or 'strip'");
0796: val = Validation.STRIP;
0797: }
0798: ((StaticQueryContext) env).setConstructionMode(val);
0799: nextToken();
0800: }
0801:
0802: /**
0803: * Parse (and process) the schema import declaration.
0804: * SchemaImport ::= "import" "schema" SchemaPrefix? URILiteral ("at" URILiteral ("," URILiteral)*)?
0805: * SchemaPrefix ::= ("namespace" NCName "=") | ("default" "element" "namespace")
0806: */
0807:
0808: private void parseSchemaImport() throws StaticError {
0809: if (!env.getConfiguration().isSchemaAware(Configuration.XQUERY)) {
0810: grumble(
0811: "To import a schema, you need the schema-aware version of Saxon",
0812: "XQST0009");
0813: }
0814: Import sImport = new Import();
0815: String prefix = null;
0816: sImport.namespaceURI = null;
0817: sImport.locationURIs = new ArrayList(5);
0818: nextToken();
0819: if (isKeyword("namespace")) {
0820: nextToken();
0821: expect(Token.NAME);
0822: prefix = t.currentTokenValue;
0823: checkProhibitedPrefixes(prefix);
0824: nextToken();
0825: expect(Token.EQUALS);
0826: nextToken();
0827: } else if (isKeyword("default")) {
0828: nextToken();
0829: if (!isKeyword("element")) {
0830: grumble("In 'import schema', expected 'element namespace'");
0831: }
0832: nextToken();
0833: if (!isKeyword("namespace")) {
0834: grumble("In 'import schema', expected keyword 'namespace'");
0835: }
0836: nextToken();
0837: prefix = "";
0838: }
0839: if (t.currentToken == Token.STRING_LITERAL) {
0840: sImport.namespaceURI = URILiteral(t.currentTokenValue);
0841: nextToken();
0842: if (isKeyword("at")) {
0843: nextToken();
0844: expect(Token.STRING_LITERAL);
0845: sImport.locationURIs
0846: .add(URILiteral(t.currentTokenValue));
0847: nextToken();
0848: while (t.currentToken == Token.COMMA) {
0849: nextToken();
0850: expect(Token.STRING_LITERAL);
0851: sImport.locationURIs
0852: .add(URILiteral(t.currentTokenValue));
0853: nextToken();
0854: }
0855: } else if (t.currentToken != Token.SEMICOLON) {
0856: grumble("After the target namespace URI, expected 'at' or ';'");
0857: }
0858: } else {
0859: grumble("After 'import schema', expected 'namespace', 'default', or a string-literal");
0860: }
0861: if (prefix != null) {
0862: try {
0863: if ("".equals(prefix)) {
0864: ((StaticQueryContext) env)
0865: .setDefaultElementNamespace(sImport.namespaceURI);
0866: } else {
0867: if (sImport.namespaceURI == null
0868: || "".equals(sImport.namespaceURI)) {
0869: grumble(
0870: "A prefix cannot be bound to the null namespace",
0871: "XQST0057");
0872: }
0873: ((StaticQueryContext) env).declarePassiveNamespace(
0874: prefix, sImport.namespaceURI, true);
0875: }
0876: } catch (StaticError err) {
0877: err.setLocator(makeLocator());
0878: reportError(err);
0879: }
0880: }
0881: schemaImports.add(sImport);
0882:
0883: }
0884:
0885: private void applySchemaImport(Import sImport) throws StaticError {
0886:
0887: // Do the importing
0888:
0889: Configuration config = env.getConfiguration();
0890: if (config.getSchema(sImport.namespaceURI) == null) {
0891: if (sImport.locationURIs.size() > 0) {
0892: try {
0893: PipelineConfiguration pipe = config
0894: .makePipelineConfiguration();
0895: config.readMultipleSchemas(pipe, env.getBaseURI(),
0896: sImport.locationURIs, sImport.namespaceURI);
0897: namespacesToBeSealed.add(sImport.namespaceURI);
0898: } catch (TransformerConfigurationException err) {
0899: grumble("Error in schema. " + err.getMessage(),
0900: "XQST0059");
0901: }
0902: } else {
0903: grumble("Unable to locate requested schema", "XQST0059");
0904: }
0905: }
0906: ((StaticQueryContext) env)
0907: .addImportedSchema(sImport.namespaceURI);
0908: }
0909:
0910: /**
0911: * Parse (and expand) the module import declaration.
0912: * Syntax: <"import" "module" ("namespace" NCName "=")? uri ("at" uri ("," uri)*)? ";"
0913: */
0914:
0915: private void parseModuleImport() throws StaticError {
0916: StaticQueryContext this Module = (StaticQueryContext) env;
0917: Import mImport = new Import();
0918: String prefix = null;
0919: mImport.namespaceURI = null;
0920: mImport.locationURIs = new ArrayList(5);
0921: nextToken();
0922: if (t.currentToken == Token.NAME
0923: && t.currentTokenValue == "namespace") {
0924: nextToken();
0925: expect(Token.NAME);
0926: prefix = t.currentTokenValue;
0927: checkProhibitedPrefixes(prefix);
0928: nextToken();
0929: expect(Token.EQUALS);
0930: nextToken();
0931: }
0932: if (t.currentToken == Token.STRING_LITERAL) {
0933: mImport.namespaceURI = URILiteral(t.currentTokenValue);
0934: if (mImport.namespaceURI.equals("")) {
0935: grumble("Imported module namespace cannot be \"\"",
0936: "XQST0008");
0937: mImport.namespaceURI = "http://saxon.fallback.namespace/line"
0938: + t.getLineNumber(); // for error recovery
0939: }
0940: if (importedModules.contains(mImport.namespaceURI)) {
0941: grumble(
0942: "Two 'import module' declarations specify the same module namespace",
0943: "XQST0047");
0944: }
0945: importedModules.add(mImport.namespaceURI);
0946: ((StaticQueryContext) env)
0947: .addImportedNamespace(mImport.namespaceURI);
0948: nextToken();
0949: if (isKeyword("at")) {
0950: do {
0951: nextToken();
0952: expect(Token.STRING_LITERAL);
0953: String uri = URILiteral(t.currentTokenValue);
0954: mImport.locationURIs.add(uri);
0955: nextToken();
0956: } while (t.currentToken == Token.COMMA);
0957: }
0958: } else {
0959: grumble("After 'import module', expected 'namespace' or a string-literal");
0960: }
0961: if (prefix != null) {
0962: try {
0963: this Module.declarePassiveNamespace(prefix,
0964: mImport.namespaceURI, true);
0965: } catch (StaticError err) {
0966: err.setLocator(makeLocator());
0967: reportError(err);
0968: }
0969: }
0970:
0971: // Check that this import would not create a cycle involving a change of namespace
0972: if (!mImport.namespaceURI.equals(((StaticQueryContext) env)
0973: .getModuleNamespace())) {
0974: StaticQueryContext parent = (StaticQueryContext) env;
0975: if (!parent.mayImport(mImport.namespaceURI)) {
0976: StaticError err = new StaticError(
0977: "A module cannot import itself directly or indirectly, unless all modules in the cycle are in the same namespace");
0978: err.setErrorCode("XQST0073");
0979: throw err;
0980: }
0981: }
0982:
0983: moduleImports.add(mImport);
0984: }
0985:
0986: public void applyModuleImport(Import mImport) throws StaticError {
0987: boolean foundOne = false;
0988:
0989: // resolve the location URIs against the base URI
0990: for (int i = 0; i < mImport.locationURIs.size(); i++) {
0991: try {
0992: String uri = (String) mImport.locationURIs.get(i);
0993: uri = StandardURIResolver.makeAbsolute(uri,
0994: env.getBaseURI()).toString();
0995: mImport.locationURIs.set(i, uri);
0996: } catch (DynamicError dynamicError) {
0997: grumble(dynamicError.getMessage());
0998: } catch (URISyntaxException e) {
0999: grumble("Invalid URI " + mImport.locationURIs.get(i)
1000: + ": " + e.getMessage());
1001: }
1002: }
1003:
1004: // If any of the modules are already loaded, don't re-read them; but do check that none of them
1005: // references the current module namespace directly or indirectly
1006: List existingModules = executable
1007: .getQueryLibraryModules(mImport.namespaceURI);
1008: if (existingModules != null) {
1009: for (int m = 0; m < existingModules.size(); m++) {
1010: StaticQueryContext importedModule = (StaticQueryContext) existingModules
1011: .get(m);
1012: if (!importedModule.getLocationURI().equals(
1013: ((StaticQueryContext) env).getLocationURI())) {
1014: QueryReader.importModuleContents(importedModule,
1015: (StaticQueryContext) env);
1016: foundOne = true;
1017: }
1018: if (((StaticQueryContext) env).getModuleNamespace() != null
1019: && !((StaticQueryContext) env)
1020: .getModuleNamespace().equals(
1021: importedModule
1022: .getModuleNamespace())
1023: && importedModule
1024: .importsNamespaceIndirectly(((StaticQueryContext) env)
1025: .getModuleNamespace())) {
1026: grumble("A cycle exists among the module imports, involving namespaces "
1027: + ((StaticQueryContext) env)
1028: .getModuleNamespace()
1029: + " and "
1030: + importedModule.getModuleNamespace());
1031: }
1032: for (int h = mImport.locationURIs.size() - 1; h >= 0; h--) {
1033: if (mImport.locationURIs.get(h).equals(
1034: importedModule.getLocationURI())) {
1035: mImport.locationURIs.remove(h);
1036: }
1037: }
1038: }
1039: }
1040:
1041: // If we've found at least one module, and there are no location URIs left, call it a day.
1042:
1043: if (mImport.locationURIs.size() == 0 && foundOne) {
1044: return;
1045: }
1046:
1047: // Call the module URI resolver to find the remaining modules
1048:
1049: ModuleURIResolver resolver = env.getConfiguration()
1050: .getModuleURIResolver();
1051:
1052: String[] hints = new String[mImport.locationURIs.size()];
1053: hints = (String[]) mImport.locationURIs.toArray(hints);
1054: StreamSource[] sources = null;
1055: try {
1056: if (resolver != null) {
1057: sources = resolver.resolve(mImport.namespaceURI, env
1058: .getBaseURI(), hints);
1059: }
1060: if (sources == null) {
1061: if (hints.length == 0) {
1062: if (existingModules == null) {
1063: grumble("Cannot locate module for namespace "
1064: + mImport.namespaceURI, "XQST0059");
1065: }
1066: }
1067: resolver = env.getConfiguration()
1068: .getStandardModuleURIResolver();
1069: sources = resolver.resolve(mImport.namespaceURI, env
1070: .getBaseURI(), hints);
1071: }
1072: } catch (XPathException e) {
1073: throw StaticError.makeStaticError(e);
1074: }
1075:
1076: for (int m = 0; m < sources.length; m++) {
1077: StreamSource ss = sources[m];
1078: String baseURI = ss.getSystemId();
1079: if (baseURI == null) {
1080: if (m < hints.length) {
1081: ss.setSystemId(hints[m]);
1082: } else {
1083: grumble(
1084: "No base URI available for imported module",
1085: "XQST0059");
1086: }
1087: }
1088: try {
1089: String queryText = QueryReader.readSourceQuery(ss,
1090: nameChecker);
1091: StaticQueryContext importedModule = StaticQueryContext
1092: .makeStaticQueryContext(baseURI, executable,
1093: (StaticQueryContext) env, queryText,
1094: mImport.namespaceURI);
1095: QueryReader.importModuleContents(importedModule,
1096: (StaticQueryContext) env);
1097: } catch (StaticError err) {
1098: if (err.getLocator() == null) {
1099: err.setLocator(makeLocator());
1100: }
1101: reportError(err);
1102: }
1103: }
1104: }
1105:
1106: /**
1107: * Parse the Base URI declaration.
1108: * Syntax: <"declare" "base-uri"> uri-literal
1109: *
1110: * @throws net.sf.saxon.trans.StaticError
1111: */
1112:
1113: private void parseBaseURIDeclaration() throws StaticError {
1114: if (foundBaseURIDeclaration) {
1115: grumble("Base URI Declaration may only appear once",
1116: "XQST0032");
1117: }
1118: foundBaseURIDeclaration = true;
1119: nextToken();
1120: expect(Token.STRING_LITERAL);
1121: String uri = URILiteral(t.currentTokenValue);
1122: ((StaticQueryContext) env).setBaseURI(uri);
1123: nextToken();
1124: }
1125:
1126: /**
1127: * Parse the "default function namespace" declaration.
1128: * Syntax: <"declare" "default" "function" "namespace"> StringLiteral
1129: *
1130: * @throws net.sf.saxon.trans.StaticError to indicate a syntax error
1131: */
1132:
1133: private void parseDefaultFunctionNamespace() throws StaticError {
1134: if (foundDefaultFunctionNamespace) {
1135: grumble(
1136: "default function namespace appears more than once",
1137: "XQST0066");
1138: }
1139: foundDefaultFunctionNamespace = true;
1140: nextToken();
1141: expect(Token.NAME);
1142: if (!"namespace".equals(t.currentTokenValue)) {
1143: grumble("After 'declare default function', expected 'namespace'");
1144: }
1145: nextToken();
1146: expect(Token.STRING_LITERAL);
1147: String uri = URILiteral(t.currentTokenValue);
1148: ((StaticQueryContext) env).setDefaultFunctionNamespace(uri);
1149: nextToken();
1150: }
1151:
1152: /**
1153: * Parse the "default element namespace" declaration.
1154: * Syntax: <"declare" "default" "element" "namespace"> StringLiteral
1155: *
1156: * @throws net.sf.saxon.trans.StaticError to indicate a syntax error
1157: */
1158:
1159: private void parseDefaultElementNamespace() throws StaticError {
1160: if (foundDefaultElementNamespace) {
1161: grumble("default element namespace appears more than once",
1162: "XQST0066");
1163: }
1164: foundDefaultElementNamespace = true;
1165: nextToken();
1166: expect(Token.NAME);
1167: if (!"namespace".equals(t.currentTokenValue)) {
1168: grumble("After 'declare default element', expected 'namespace'");
1169: }
1170: nextToken();
1171: expect(Token.STRING_LITERAL);
1172: String uri = URILiteral(t.currentTokenValue);
1173: ((StaticQueryContext) env).setDefaultElementNamespace(uri);
1174: nextToken();
1175: }
1176:
1177: /**
1178: * Parse a namespace declaration in the Prolog.
1179: * Syntax: <"declare" "namespace"> NCName "=" StringLiteral
1180: *
1181: * @throws net.sf.saxon.trans.StaticError
1182: */
1183:
1184: private void parseNamespaceDeclaration() throws StaticError {
1185: nextToken();
1186: expect(Token.NAME);
1187: String prefix = t.currentTokenValue;
1188: if (!nameChecker.isValidNCName(prefix)) {
1189: grumble("Invalid namespace prefix " + Err.wrap(prefix));
1190: }
1191: checkProhibitedPrefixes(prefix);
1192: nextToken();
1193: expect(Token.EQUALS);
1194: nextToken();
1195: expect(Token.STRING_LITERAL);
1196: String uri = URILiteral(t.currentTokenValue);
1197: try {
1198: ((StaticQueryContext) env).declarePassiveNamespace(prefix,
1199: uri, true);
1200: } catch (StaticError err) {
1201: err.setLocator(makeLocator());
1202: reportError(err);
1203: }
1204: nextToken();
1205: }
1206:
1207: /**
1208: * Check that a namespace prefix is not a prohibited prefix (xml or xmlns)
1209: *
1210: * @param prefix the prefix to be tested
1211: * @throws StaticError if the prefix is prohibited
1212: */
1213:
1214: private void checkProhibitedPrefixes(String prefix)
1215: throws StaticError {
1216: if ("xml".equals(prefix) || "xmlns".equals(prefix)) {
1217: grumble("The namespace prefix " + Err.wrap(prefix)
1218: + " cannot be redeclared", "XQST0070");
1219: }
1220: }
1221:
1222: /**
1223: * Parse a global variable definition.
1224: * <"declare" "variable" "$"> VarName TypeDeclaration?
1225: * ((":=" Expr ) | "external")
1226: * Currently accept both
1227: *
1228: * @throws net.sf.saxon.trans.StaticError
1229: */
1230:
1231: private void parseVariableDeclaration() throws StaticError {
1232: int offset = t.currentTokenStartOffset;
1233: GlobalVariableDefinition var = new GlobalVariableDefinition();
1234: var.setLineNumber(t.getLineNumber());
1235: var.setSystemId(env.getSystemId());
1236: nextToken();
1237: expect(Token.DOLLAR);
1238: t.setState(Tokenizer.BARE_NAME_STATE);
1239: nextToken();
1240: expect(Token.NAME);
1241: String varName = t.currentTokenValue;
1242: var.setVariableName(varName);
1243: int varNameCode = makeNameCode(t.currentTokenValue, false);
1244: int varFingerprint = varNameCode & 0xfffff;
1245: var.setNameCode(varFingerprint);
1246:
1247: String uri = env.getNamePool().getURI(varNameCode);
1248: String moduleURI = ((StaticQueryContext) env)
1249: .getModuleNamespace();
1250: if (moduleURI != null && !moduleURI.equals(uri)) {
1251: grumble(
1252: "A variable declared in a library module must be in the module namespace",
1253: "XQST0048");
1254: }
1255:
1256: nextToken();
1257: SequenceType requiredType = SequenceType.ANY_SEQUENCE;
1258: if (isKeyword("as")) {
1259: t.setState(Tokenizer.SEQUENCE_TYPE_STATE);
1260: nextToken();
1261: requiredType = parseSequenceType();
1262: }
1263: var.setRequiredType(requiredType);
1264:
1265: if (t.currentToken == Token.ASSIGN) {
1266: t.setState(Tokenizer.DEFAULT_STATE);
1267: nextToken();
1268: Expression exp = parseExpression();
1269: var.setIsParameter(false);
1270: var.setValueExpression(makeTracer(offset, exp,
1271: StandardNames.XSL_VARIABLE, varNameCode));
1272: } else if (t.currentToken == Token.NAME) {
1273: if ("external".equals(t.currentTokenValue)) {
1274: var.setIsParameter(true);
1275: if (defaultValue != null) {
1276: var.setValueExpression(defaultValue);
1277: }
1278: nextToken();
1279: } else {
1280: grumble("Variable must either be initialized or be declared as external");
1281: }
1282: } else {
1283: grumble("Expected ':=' or 'external' in variable declaration");
1284: }
1285:
1286: StaticQueryContext qenv = (StaticQueryContext) env;
1287: if (qenv.getModuleNamespace() != null
1288: && env.getNamePool().getURICode(varFingerprint) != qenv
1289: .getModuleNamespaceCode()) {
1290: grumble("Variable " + Err.wrap(varName, Err.VARIABLE)
1291: + " is not defined in the module namespace");
1292: }
1293: try {
1294: qenv.declareVariable(var);
1295: } catch (XPathException e) {
1296: grumble(e.getMessage(), e.getErrorCodeLocalPart());
1297: }
1298: }
1299:
1300: /**
1301: * Parse a function declaration.
1302: * <p>Syntax:<br/>
1303: * <"declare" "function"> <QName "("> ParamList? (")" | (<")" "as"> SequenceType))
1304: * (EnclosedExpr | "external")
1305: * </p>
1306: * <p>On entry, the "define function" has already been recognized</p>
1307: *
1308: * @throws net.sf.saxon.trans.StaticError if a syntax error is found
1309: */
1310:
1311: private void parseFunctionDeclaration() throws StaticError {
1312: // the next token should be the < QNAME "("> pair
1313: int offset = t.currentTokenStartOffset;
1314: nextToken();
1315: expect(Token.FUNCTION);
1316:
1317: String uri;
1318: int fnc;
1319: if (t.currentTokenValue.indexOf(':') < 0) {
1320: uri = env.getDefaultFunctionNamespace();
1321: fnc = env.getNamePool().allocate("", uri,
1322: t.currentTokenValue);
1323: } else {
1324: fnc = makeNameCode(t.currentTokenValue, false);
1325: uri = env.getNamePool().getURI(fnc);
1326: }
1327:
1328: if (uri.equals("")) {
1329: grumble("The function must be in a namespace", "XQST0060");
1330: }
1331:
1332: String moduleURI = ((StaticQueryContext) env)
1333: .getModuleNamespace();
1334: if (moduleURI != null && !moduleURI.equals(uri)) {
1335: grumble(
1336: "A function in a library module must be in the module namespace",
1337: "XQST0048");
1338: }
1339:
1340: if (NamespaceConstant.isReservedInQuery(uri)) {
1341: grumble("The function name " + t.currentTokenValue
1342: + " is in a reserved namespace", "XQST0045");
1343: }
1344:
1345: XQueryFunction func = new XQueryFunction();
1346: func.setNameCode(fnc);
1347: func.arguments = new ArrayList(8);
1348: func.resultType = SequenceType.ANY_SEQUENCE;
1349: func.body = null;
1350: func.lineNumber = t.getLineNumber(offset);
1351: func.columnNumber = t.getColumnNumber(offset);
1352: func.systemId = env.getSystemId();
1353: func.setExecutable(getExecutable());
1354:
1355: nextToken();
1356: IntHashSet paramNames = new IntHashSet(8);
1357: while (t.currentToken != Token.RPAR) {
1358: // ParamList ::= Param ("," Param)*
1359: // Param ::= "$" VarName TypeDeclaration?
1360: expect(Token.DOLLAR);
1361: nextToken();
1362: expect(Token.NAME);
1363: String argName = t.currentTokenValue;
1364: int nameCode = makeNameCode(argName, false);
1365: int fingerprint = nameCode & 0xfffff;
1366: if (paramNames.contains(fingerprint)) {
1367: grumble("Duplicate parameter name "
1368: + Err.wrap(t.currentTokenValue, Err.VARIABLE),
1369: "XQST0039");
1370: }
1371: paramNames.add(fingerprint);
1372: SequenceType paramType = SequenceType.ANY_SEQUENCE;
1373: nextToken();
1374: if (t.currentToken == Token.NAME
1375: && "as".equals(t.currentTokenValue)) {
1376: nextToken();
1377: paramType = parseSequenceType();
1378: }
1379:
1380: RangeVariableDeclaration arg = new RangeVariableDeclaration();
1381: arg.setNameCode(nameCode);
1382: arg.setRequiredType(paramType);
1383: arg.setVariableName(argName);
1384: func.arguments.add(arg);
1385: declareRangeVariable(arg);
1386: if (t.currentToken == Token.RPAR) {
1387: break;
1388: } else if (t.currentToken == Token.COMMA) {
1389: nextToken();
1390: } else {
1391: grumble("Expected ',' or ')' after function argument, found '"
1392: + Token.tokens[t.currentToken] + '\'');
1393: }
1394: }
1395: t.setState(Tokenizer.BARE_NAME_STATE);
1396: nextToken();
1397: if (isKeyword("as")) {
1398: t.setState(Tokenizer.SEQUENCE_TYPE_STATE);
1399: nextToken();
1400: func.resultType = parseSequenceType();
1401: }
1402: if (isKeyword("external")) {
1403: grumble("Saxon does not allow external functions to be declared");
1404: } else {
1405: expect(Token.LCURLY);
1406: t.setState(Tokenizer.DEFAULT_STATE);
1407: nextToken();
1408: func.body = parseExpression();
1409: if (func.body instanceof ComputedExpression) {
1410: ((ComputedExpression) func.body)
1411: .setParentExpression(func);
1412: }
1413: expect(Token.RCURLY);
1414: lookAhead(); // must be done manually after an RCURLY
1415: }
1416: UserFunctionParameter[] params = func.getParameterDefinitions();
1417: for (int i = 0; i < params.length; i++) {
1418: undeclareRangeVariable();
1419: }
1420: t.setState(Tokenizer.DEFAULT_STATE);
1421: nextToken();
1422:
1423: StaticQueryContext qenv = (StaticQueryContext) env;
1424:
1425: try {
1426: qenv.declareFunction(func);
1427: } catch (XPathException e) {
1428: grumble(e.getMessage(), e.getErrorCodeLocalPart());
1429: }
1430:
1431: }
1432:
1433: /**
1434: * Parse an option declaration.
1435: * <p>Syntax:<br/>
1436: * <"declare" "option"> QName "string-literal"
1437: * </p>
1438: * <p>On entry, the "declare option" has already been recognized</p>
1439: *
1440: * @throws net.sf.saxon.trans.StaticError if a syntax error is found
1441: */
1442:
1443: private void parseOptionDeclaration() throws StaticError {
1444: nextToken();
1445: expect(Token.NAME);
1446: int varNameCode = makeNameCode(t.currentTokenValue, false);
1447: String uri = env.getNamePool().getURI(varNameCode);
1448:
1449: nextToken();
1450: expect(Token.STRING_LITERAL);
1451: String value = URILiteral(t.currentTokenValue);
1452:
1453: if (uri.equals(NamespaceConstant.SAXON)) {
1454: String localName = env.getNamePool().getLocalName(
1455: varNameCode);
1456: if (localName.equals("output")) {
1457: setOutputProperty(value);
1458: } else if (localName.equals("default")) {
1459: defaultValue = setDefaultValue(value);
1460: } else {
1461: warning("Unknown Saxon option declaration: "
1462: + env.getNamePool().getDisplayName(varNameCode));
1463: }
1464: }
1465:
1466: nextToken();
1467: }
1468:
1469: /**
1470: * Handle a saxon:output option declaration. Format:
1471: * declare option saxon:output "indent = yes"
1472: */
1473:
1474: private void setOutputProperty(String property) {
1475: int equals = property.indexOf("=");
1476: if (equals < 0) {
1477: badOutputProperty("no equals sign");
1478: } else if (equals == 0) {
1479: badOutputProperty("starts with '=");
1480: } else if (equals == property.length() - 1) {
1481: badOutputProperty("ends with '=");
1482: }
1483: String keyword = property.substring(0, equals).trim();
1484: String value = property.substring(equals + 1).trim();
1485:
1486: Properties props = getExecutable().getDefaultOutputProperties();
1487: try {
1488: int key = makeNameCode(keyword, false) & NamePool.FP_MASK;
1489: String lname = env.getNamePool().getLocalName(key);
1490: String uri = env.getNamePool().getURI(key);
1491: ResultDocument.setSerializationProperty(props, uri, lname,
1492: value, env.getNamespaceResolver(), false,
1493: nameChecker);
1494: } catch (XPathException e) {
1495: badOutputProperty(e.getMessage());
1496: }
1497: }
1498:
1499: private void badOutputProperty(String s) {
1500: try {
1501: warning("Invalid serialization property (" + s
1502: + ") - ignored");
1503: } catch (StaticError staticError) {
1504: //
1505: }
1506: }
1507:
1508: /**
1509: * Parse the expression (inside a string literal) used to define default values
1510: * for external variables. This requires instantiating a nested XPath parser.
1511: */
1512:
1513: public Expression setDefaultValue(String exp) {
1514: try {
1515: IndependentContext ic = new IndependentContext(env
1516: .getConfiguration());
1517: ic.setNamespaceResolver(env.getNamespaceResolver());
1518: Expression expr = ExpressionTool.make(exp, ic, 0,
1519: Token.EOF, 1);
1520:
1521: ItemType contextItemType = Type.ITEM_TYPE;
1522: expr = expr.typeCheck(ic, contextItemType);
1523: expr = expr.optimize(env.getConfiguration().getOptimizer(),
1524: env, contextItemType);
1525: SlotManager stackFrameMap = ic.getStackFrameMap();
1526: ExpressionTool.allocateSlots(expr, stackFrameMap
1527: .getNumberOfVariables(), stackFrameMap);
1528: return expr;
1529: } catch (XPathException e) {
1530: try {
1531: warning("Invalid expression for default value: "
1532: + e.getMessage() + " (ignored)");
1533: } catch (StaticError staticError) {
1534: //
1535: }
1536: return null;
1537: }
1538: }
1539:
1540: /**
1541: * Parse a FLWOR expression. This replaces the XPath "for" expression.
1542: * Full syntax:
1543: * <p/>
1544: * [41] FLWORExpr ::= (ForClause | LetClause)+
1545: * WhereClause? OrderByClause?
1546: * "return" ExprSingle
1547: * [42] ForClause ::= <"for" "$"> VarName TypeDeclaration? PositionalVar? "in" ExprSingle
1548: * ("," "$" VarName TypeDeclaration? PositionalVar? "in" ExprSingle)*
1549: * [43] PositionalVar ::= "at" "$" VarName
1550: * [44] LetClause ::= <"let" "$"> VarName TypeDeclaration? ":=" ExprSingle
1551: * ("," "$" VarName TypeDeclaration? ":=" ExprSingle)*
1552: * [45] WhereClause ::= "where" Expr
1553: * [46] OrderByClause ::= (<"order" "by"> | <"stable" "order" "by">) OrderSpecList
1554: * [47] OrderSpecList ::= OrderSpec ("," OrderSpec)*
1555: * [48] OrderSpec ::= ExprSingle OrderModifier
1556: * [49] OrderModifier ::= ("ascending" | "descending")?
1557: * (<"empty" "greatest"> | <"empty" "least">)?
1558: * ("collation" StringLiteral)?
1559: * </p>
1560: *
1561: * @return the resulting subexpression
1562: * @throws net.sf.saxon.trans.StaticError if any error is encountered
1563: */
1564:
1565: protected Expression parseForExpression() throws StaticError {
1566: int offset = t.currentTokenStartOffset;
1567: Expression whereCondition = null;
1568: int whereOffset = -1;
1569: //boolean stableOrder = false;
1570: List clauseList = new ArrayList(4);
1571: while (true) {
1572: if (t.currentToken == Token.FOR) {
1573: parseForClause(clauseList);
1574: } else if (t.currentToken == Token.LET) {
1575: parseLetClause(clauseList);
1576: } else {
1577: break;
1578: }
1579: }
1580: if (t.currentToken == Token.WHERE || isKeyword("where")) {
1581: whereOffset = t.currentTokenStartOffset;
1582: nextToken();
1583: whereCondition = parseExprSingle();
1584: }
1585: int orderByOffset = t.currentTokenStartOffset;
1586: if (isKeyword("stable")) {
1587: // we read the "stable" keyword but ignore it; Saxon ordering is always stable
1588: nextToken();
1589: if (!isKeyword("order")) {
1590: grumble("'stable' must be followed by 'order by'");
1591: }
1592: }
1593: List sortSpecList = null;
1594: if (isKeyword("order")) {
1595: t.setState(Tokenizer.BARE_NAME_STATE);
1596: nextToken();
1597: if (!isKeyword("by")) {
1598: grumble("'order' must be followed by 'by'");
1599: }
1600: t.setState(Tokenizer.DEFAULT_STATE);
1601: nextToken();
1602: sortSpecList = parseSortDefinition();
1603: }
1604: int returnOffset = t.currentTokenStartOffset;
1605: expect(Token.RETURN);
1606: t.setState(Tokenizer.DEFAULT_STATE);
1607: nextToken();
1608: Expression action = parseExprSingle();
1609: action = makeTracer(returnOffset, action,
1610: Location.RETURN_EXPRESSION, -1);
1611:
1612: // If there is an order by clause, we modify the "return" expression so that it
1613: // returns a tuple containing the actual return value, plus the value of
1614: // each of the sort keys. We then wrap the entire FLWR expression inside a
1615: // TupleSorter that sorts the stream of tuples according to the sort keys,
1616: // discarding the sort keys and returning only the true result. The tuple
1617: // is implemented as a Java object wrapped inside an ObjectValue, which is
1618: // a general-purpose wrapper for objects that don't fit in the XPath type system.
1619:
1620: if (sortSpecList != null) {
1621: TupleExpression exp = new TupleExpression(1 + sortSpecList
1622: .size());
1623: setLocation(exp);
1624: exp.setExpression(0, action);
1625: for (int i = 0; i < sortSpecList.size(); i++) {
1626: try {
1627: RoleLocator role = new RoleLocator(
1628: RoleLocator.ORDER_BY, "FLWR", i, null);
1629: role.setSourceLocator(makeLocator());
1630: Expression sk = TypeChecker.staticTypeCheck(
1631: ((SortSpec) sortSpecList.get(i)).sortKey,
1632: SequenceType.OPTIONAL_ATOMIC, false, role,
1633: env);
1634: exp.setExpression(i + 1, sk);
1635: } catch (XPathException err) {
1636: grumble(err.getMessage());
1637: }
1638: }
1639: action = exp;
1640: }
1641:
1642: // if there is a "where" condition, we implement this by wrapping an if/then/else
1643: // around the "return" expression. No clever optimization yet!
1644:
1645: if (whereCondition != null) {
1646: action = new IfExpression(whereCondition, action,
1647: EmptySequence.getInstance());
1648: action = makeTracer(whereOffset, action,
1649: Location.WHERE_CLAUSE, -1);
1650: setLocation(action);
1651: }
1652:
1653: for (int i = clauseList.size() - 1; i >= 0; i--) {
1654: Object clause = clauseList.get(i);
1655: if (clause instanceof ExpressionParser.ForClause) {
1656: ExpressionParser.ForClause fc = (ExpressionParser.ForClause) clause;
1657: ForExpression exp = new ForExpression();
1658: exp.setVariableDeclaration(fc.rangeVariable);
1659: exp.setPositionVariable(fc.positionVariable);
1660: exp.setLocationId(env.getLocationMap()
1661: .allocateLocationId(env.getSystemId(),
1662: t.getLineNumber(fc.offset)));
1663: exp.setSequence(fc.sequence);
1664: exp.setAction(action);
1665: action = makeTracer(fc.offset, exp,
1666: Location.FOR_EXPRESSION, fc.rangeVariable
1667: .getNameCode());
1668: } else {
1669: LetClause lc = (LetClause) clause;
1670: LetExpression exp = makeLetExpression();
1671: exp.setVariableDeclaration(lc.variable);
1672: exp.setLocationId(env.getLocationMap()
1673: .allocateLocationId(env.getSystemId(),
1674: t.getLineNumber(lc.offset)));
1675: exp.setSequence(lc.value);
1676: exp.setAction(action);
1677: action = makeTracer(lc.offset, exp,
1678: Location.LET_EXPRESSION, lc.variable
1679: .getNameCode());
1680: }
1681: }
1682:
1683: // Now wrap the whole expression in a TupleSorter if there is a sort specification
1684:
1685: if (sortSpecList != null) {
1686: FixedSortKeyDefinition[] keys = new FixedSortKeyDefinition[sortSpecList
1687: .size()];
1688: for (int i = 0; i < sortSpecList.size(); i++) {
1689: SortSpec spec = (SortSpec) sortSpecList.get(i);
1690: FixedSortKeyDefinition key = new FixedSortKeyDefinition();
1691: //key.setSortKey(((SortSpec) sortSpecList.get(i)).sortKey);
1692: key.setSortKey(StringValue.EMPTY_STRING);
1693: key.setOrder(new StringValue(
1694: spec.ascending ? "ascending" : "descending"));
1695: key.setEmptyFirst(spec.ascending ? spec.emptyLeast
1696: : !spec.emptyLeast);
1697:
1698: if (spec.collation != null) {
1699: final Comparator comparator = env
1700: .getCollation(spec.collation);
1701: if (comparator == null) {
1702: grumble("Unknown collation '" + spec.collation
1703: + '\'', "XQST0076");
1704: }
1705: key.setCollation(comparator);
1706: }
1707: try {
1708: key.bindComparer(env.makeEarlyEvaluationContext());
1709: keys[i] = key;
1710: } catch (XPathException e) {
1711: grumble(e.getMessage());
1712: }
1713: }
1714: TupleSorter sorter = new TupleSorter(action, keys);
1715: setLocation(sorter);
1716: action = makeTracer(orderByOffset, sorter,
1717: Location.ORDER_BY_CLAUSE, -1);
1718: }
1719:
1720: // undeclare all the range variables
1721:
1722: for (int i = clauseList.size() - 1; i >= 0; i--) {
1723: Object clause = clauseList.get(i);
1724: if ((clause instanceof ExpressionParser.ForClause)
1725: && ((ExpressionParser.ForClause) clause).positionVariable != null) {
1726: // undeclare the "at" variable if it was declared
1727: undeclareRangeVariable();
1728: }
1729: // undeclare the primary variable
1730: undeclareRangeVariable();
1731: }
1732:
1733: setLocation(action, offset);
1734: return action;
1735:
1736: }
1737:
1738: /**
1739: * Make a LetExpression. This returns an ordinary LetExpression if tracing is off, and an EagerLetExpression
1740: * if tracing is on. This is so that trace events occur in an order that the user can follow.
1741: */
1742:
1743: private LetExpression makeLetExpression() {
1744: if (env.getConfiguration().getTraceListener() == null) {
1745: return new LetExpression();
1746: } else {
1747: return new EagerLetExpression();
1748: }
1749: }
1750:
1751: /**
1752: * Parse a ForClause.
1753: * <p/>
1754: * [42] ForClause ::= <"for" "$"> VarName TypeDeclaration? PositionalVar? "in" ExprSingle
1755: * ("," "$" VarName TypeDeclaration? PositionalVar? "in" ExprSingle)*
1756: * </p>
1757: *
1758: * @param clauseList - the components of the parsed ForClause are appended to the
1759: * supplied list
1760: * @throws net.sf.saxon.trans.StaticError
1761: */
1762: private void parseForClause(List clauseList) throws StaticError {
1763: boolean first = true;
1764: do {
1765: ExpressionParser.ForClause clause = new ExpressionParser.ForClause();
1766: if (first) {
1767: clause.offset = t.currentTokenStartOffset;
1768: }
1769: clauseList.add(clause);
1770: nextToken();
1771: if (first) {
1772: first = false;
1773: } else {
1774: clause.offset = t.currentTokenStartOffset;
1775: }
1776: expect(Token.DOLLAR);
1777: nextToken();
1778: expect(Token.NAME);
1779: String var = t.currentTokenValue;
1780:
1781: RangeVariableDeclaration v = new RangeVariableDeclaration();
1782: v.setNameCode(makeNameCode(var, false));
1783: v.setRequiredType(SequenceType.SINGLE_ITEM);
1784: v.setVariableName(var);
1785: clause.rangeVariable = v;
1786: nextToken();
1787:
1788: if (isKeyword("as")) {
1789: nextToken();
1790: SequenceType type = parseSequenceType();
1791: v.setRequiredType(type);
1792: if (type.getCardinality() != StaticProperty.EXACTLY_ONE) {
1793: grumble(
1794: "Cardinality of range variable must be exactly one",
1795: "XPTY0004");
1796: }
1797: }
1798: clause.positionVariable = null;
1799: if (isKeyword("at")) {
1800: nextToken();
1801: expect(Token.DOLLAR);
1802: nextToken();
1803: expect(Token.NAME);
1804: RangeVariableDeclaration pos = new RangeVariableDeclaration();
1805: pos
1806: .setNameCode(makeNameCode(t.currentTokenValue,
1807: false));
1808: pos.setRequiredType(SequenceType.SINGLE_INTEGER);
1809: pos.setVariableName(t.currentTokenValue);
1810: clause.positionVariable = pos;
1811: //declareRangeVariable(pos);
1812: nextToken();
1813: }
1814: expect(Token.IN);
1815: nextToken();
1816: clause.sequence = parseExprSingle();
1817: declareRangeVariable(clause.rangeVariable);
1818: if (clause.positionVariable != null) {
1819: declareRangeVariable(clause.positionVariable);
1820: }
1821: } while (t.currentToken == Token.COMMA);
1822: }
1823:
1824: /**
1825: * Parse a LetClause.
1826: * <p/>
1827: * [44] LetClause ::= <"let" "$"> VarName TypeDeclaration? ":=" ExprSingle
1828: * ("," "$" VarName TypeDeclaration? ":=" ExprSingle)*
1829: * </p>
1830: *
1831: * @param clauseList - the components of the parsed LetClause are appended to the
1832: * supplied list
1833: * @throws net.sf.saxon.trans.StaticError
1834: */
1835: private void parseLetClause(List clauseList) throws StaticError {
1836: boolean first = true;
1837: do {
1838: LetClause clause = new LetClause();
1839: if (first) {
1840: clause.offset = t.currentTokenStartOffset;
1841: }
1842: clauseList.add(clause);
1843: nextToken();
1844: if (first) {
1845: first = false;
1846: } else {
1847: clause.offset = t.currentTokenStartOffset;
1848: }
1849: expect(Token.DOLLAR);
1850: nextToken();
1851: expect(Token.NAME);
1852: String var = t.currentTokenValue;
1853:
1854: RangeVariableDeclaration v = new RangeVariableDeclaration();
1855: v.setNameCode(makeNameCode(var, false));
1856: v.setRequiredType(SequenceType.ANY_SEQUENCE);
1857: v.setVariableName(var);
1858: clause.variable = v;
1859: nextToken();
1860:
1861: if (isKeyword("as")) {
1862: nextToken();
1863: v.setRequiredType(parseSequenceType());
1864: }
1865:
1866: expect(Token.ASSIGN);
1867: nextToken();
1868: clause.value = parseExprSingle();
1869: declareRangeVariable(v);
1870: } while (t.currentToken == Token.COMMA);
1871: }
1872:
1873: /**
1874: * Make a string-join expression that concatenates the string-values of items in
1875: * a sequence with intervening spaces. This may be simplified later as a result
1876: * of type-checking.
1877: */
1878:
1879: public static Expression makeStringJoin(Expression exp,
1880: StaticContext env) {
1881:
1882: exp = new Atomizer(exp, env.getConfiguration());
1883: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
1884: ItemType t = exp.getItemType(th);
1885: if (t != Type.STRING_TYPE && t != Type.UNTYPED_ATOMIC_TYPE) {
1886: exp = new AtomicSequenceConverter(exp, Type.STRING_TYPE);
1887: }
1888:
1889: StringJoin fn = (StringJoin) SystemFunction.makeSystemFunction(
1890: "string-join", 2, env.getNamePool());
1891: Expression[] args = new Expression[2];
1892: args[0] = exp;
1893: args[1] = StringValue.SINGLE_SPACE;
1894: fn.setArguments(args);
1895: if (exp instanceof ComputedExpression) {
1896: fn
1897: .setLocationId(((ComputedExpression) exp)
1898: .getLocationId());
1899: }
1900: return fn;
1901: }
1902:
1903: private static class LetClause {
1904: public RangeVariableDeclaration variable;
1905: public Expression value;
1906: public int offset;
1907: }
1908:
1909: /**
1910: * Parse the "order by" clause.
1911: * [46] OrderByClause ::= (<"order" "by"> | <"stable" "order" "by">) OrderSpecList
1912: * [47] OrderSpecList ::= OrderSpec ("," OrderSpec)*
1913: * [48] OrderSpec ::= ExprSingle OrderModifier
1914: * [49] OrderModifier ::= ("ascending" | "descending")?
1915: * (<"empty" "greatest"> | <"empty" "least">)?
1916: * ("collation" StringLiteral)?
1917: *
1918: * @return a list of sort specifications (SortSpec), one per sort key
1919: * @throws net.sf.saxon.trans.StaticError
1920: */
1921: private List parseSortDefinition() throws StaticError {
1922: List sortSpecList = new ArrayList(5);
1923: while (true) {
1924: SortSpec sortSpec = new SortSpec();
1925: sortSpec.sortKey = parseExprSingle();
1926: sortSpec.ascending = true;
1927: sortSpec.emptyLeast = defaultEmptyLeast;
1928: sortSpec.collation = env.getDefaultCollationName();
1929: //t.setState(t.BARE_NAME_STATE);
1930: if (isKeyword("ascending")) {
1931: nextToken();
1932: } else if (isKeyword("descending")) {
1933: sortSpec.ascending = false;
1934: nextToken();
1935: }
1936: if (isKeyword("empty")) {
1937: nextToken();
1938: if (isKeyword("greatest")) {
1939: sortSpec.emptyLeast = false;
1940: nextToken();
1941: } else if (isKeyword("least")) {
1942: sortSpec.emptyLeast = true;
1943: nextToken();
1944: } else {
1945: grumble("'empty' must be followed by 'greatest' or 'least'");
1946: }
1947: }
1948: if (isKeyword("collation")) {
1949: nextToken();
1950: expect(Token.STRING_LITERAL);
1951: String collationName = URILiteral(t.currentTokenValue);
1952: URI collationURI;
1953: try {
1954: collationURI = new URI(collationName);
1955: if (!collationURI.isAbsolute()) {
1956: URI base = new URI(env.getBaseURI());
1957: collationURI = base.resolve(collationURI);
1958: collationName = collationURI.toString();
1959: }
1960: } catch (URISyntaxException err) {
1961: grumble("Collation name '" + collationName
1962: + "' is not a valid URI");
1963: collationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
1964: }
1965: sortSpec.collation = collationName;
1966: nextToken();
1967: }
1968: sortSpecList.add(sortSpec);
1969: if (t.currentToken == Token.COMMA) {
1970: nextToken();
1971: } else {
1972: break;
1973: }
1974: }
1975: return sortSpecList;
1976: }
1977:
1978: private static class SortSpec {
1979: public Expression sortKey;
1980: public boolean ascending;
1981: public boolean emptyLeast;
1982: public String collation;
1983: }
1984:
1985: /**
1986: * Parse a Typeswitch Expression.
1987: * This construct is XQuery-only.
1988: * TypeswitchExpr ::=
1989: * "typeswitch" "(" Expr ")"
1990: * CaseClause+
1991: * "default" ("$" VarName)? "return" ExprSingle
1992: * CaseClause ::=
1993: * "case" ("$" VarName "as")? SequenceType "return" Expr
1994: */
1995:
1996: protected Expression parseTypeswitchExpression() throws StaticError {
1997:
1998: // On entry, the "(" has already been read
1999: int offset = t.currentTokenStartOffset;
2000: nextToken();
2001: Expression operand = parseExpression();
2002: List types = new ArrayList(10);
2003: List actions = new ArrayList(10);
2004: expect(Token.RPAR);
2005: nextToken();
2006:
2007: // The code generated takes the form:
2008: // let $zzz := operand return
2009: // if ($zzz instance of t1) then action1
2010: // else if ($zzz instance of t2) then action2
2011: // else default-action
2012: //
2013: // If a variable is declared in a case clause or default clause,
2014: // then "action-n" takes the form
2015: // let $v as type := $zzz return action-n
2016:
2017: // we were generating "let $v as type := $zzz return action-n" but this gives a compile time error if
2018: // there's a case clause that specifies an impossible type.
2019:
2020: LetExpression outerLet = makeLetExpression();
2021:
2022: RangeVariableDeclaration gen = new RangeVariableDeclaration();
2023: gen.setNameCode(makeNameCode("zz_typeswitchVar", false));
2024: gen.setRequiredType(SequenceType.ANY_SEQUENCE);
2025: gen.setVariableName("zz_typeswitchVar");
2026:
2027: outerLet.setVariableDeclaration(gen);
2028: outerLet.setSequence(operand);
2029:
2030: while (t.currentToken == Token.CASE) {
2031: int caseOffset = t.currentTokenStartOffset;
2032: SequenceType type;
2033: Expression action;
2034: nextToken();
2035: if (t.currentToken == Token.DOLLAR) {
2036: nextToken();
2037: expect(Token.NAME);
2038: final String var = t.currentTokenValue;
2039: final int varCode = makeNameCode(var, false);
2040: nextToken();
2041: expect(Token.NAME);
2042: if (!"as".equals(t.currentTokenValue)) {
2043: grumble("After 'case $" + var + "', expected 'as'");
2044: }
2045: nextToken();
2046: type = parseSequenceType();
2047: action = makeTracer(caseOffset,
2048: parseTypeswitchReturnClause(var, varCode, gen),
2049: Location.CASE_EXPRESSION, varCode);
2050: if (action instanceof TraceExpression) {
2051: ((TraceExpression) action).setProperty("type", type
2052: .toString());
2053: }
2054:
2055: } else {
2056: type = parseSequenceType();
2057: t.treatCurrentAsOperator();
2058: expect(Token.RETURN);
2059: nextToken();
2060: action = makeTracer(caseOffset, parseExprSingle(),
2061: Location.CASE_EXPRESSION, -1);
2062: if (action instanceof TraceExpression) {
2063: ((TraceExpression) action).setProperty("type", type
2064: .toString());
2065: }
2066: }
2067: types.add(type);
2068: actions.add(action);
2069: }
2070: if (types.size() == 0) {
2071: grumble("At least one case clause is required in a typeswitch");
2072: }
2073: expect(Token.DEFAULT);
2074: final int defaultOffset = t.currentTokenStartOffset;
2075: nextToken();
2076: Expression defaultAction;
2077: if (t.currentToken == Token.DOLLAR) {
2078: nextToken();
2079: expect(Token.NAME);
2080: final String var = t.currentTokenValue;
2081: final int varCode = makeNameCode(var, false);
2082: nextToken();
2083: defaultAction = makeTracer(defaultOffset,
2084: parseTypeswitchReturnClause(var, varCode, gen),
2085: Location.DEFAULT_EXPRESSION, varCode);
2086: } else {
2087: t.treatCurrentAsOperator();
2088: expect(Token.RETURN);
2089: nextToken();
2090: defaultAction = makeTracer(defaultOffset,
2091: parseExprSingle(), Location.DEFAULT_EXPRESSION, -1);
2092: }
2093:
2094: Expression lastAction = defaultAction;
2095: for (int i = types.size() - 1; i >= 0; i--) {
2096: final VariableReference var = new VariableReference(gen);
2097: setLocation(var);
2098: final InstanceOfExpression ioe = new InstanceOfExpression(
2099: var, (SequenceType) types.get(i));
2100: setLocation(ioe);
2101: final IfExpression ife = new IfExpression(ioe,
2102: (Expression) actions.get(i), lastAction);
2103: setLocation(ife);
2104: lastAction = ife;
2105: }
2106: outerLet.setAction(lastAction);
2107: return makeTracer(offset, outerLet,
2108: Location.TYPESWITCH_EXPRESSION, -1);
2109: }
2110:
2111: private Expression parseTypeswitchReturnClause(String var,
2112: int varCode, RangeVariableDeclaration gen)
2113: throws StaticError {
2114: Expression action;
2115: t.treatCurrentAsOperator();
2116: expect(Token.RETURN);
2117: nextToken();
2118:
2119: RangeVariableDeclaration v = new RangeVariableDeclaration();
2120: v.setNameCode(varCode);
2121: v.setRequiredType(SequenceType.ANY_SEQUENCE);
2122: v.setVariableName(var);
2123:
2124: declareRangeVariable(v);
2125: action = parseExprSingle();
2126: undeclareRangeVariable();
2127:
2128: LetExpression innerLet = makeLetExpression();
2129: innerLet.setVariableDeclaration(v);
2130: innerLet.setSequence(new VariableReference(gen));
2131: innerLet.setAction(action);
2132: action = innerLet;
2133: return action;
2134: }
2135:
2136: /**
2137: * Parse a Validate Expression.
2138: * This construct is XQuery-only. The syntax allows:
2139: * validate mode? { Expr }
2140: * mode ::= "strict" | "lax"
2141: */
2142:
2143: protected Expression parseValidateExpression() throws StaticError {
2144: if (!env.getConfiguration().isSchemaAware(Configuration.XQUERY)) {
2145: grumble("To use a validate expression, you need the schema-aware processor from http://www.saxonica.com/");
2146: }
2147: int offset = t.currentTokenStartOffset;
2148: int mode = Validation.STRICT;
2149: boolean foundCurly = false;
2150: switch (t.currentToken) {
2151: case Token.VALIDATE_STRICT:
2152: mode = Validation.STRICT;
2153: nextToken();
2154: break;
2155: case Token.VALIDATE_LAX:
2156: mode = Validation.LAX;
2157: nextToken();
2158: break;
2159: case Token.KEYWORD_CURLY:
2160: if (t.currentTokenValue == "validate") {
2161: mode = Validation.STRICT;
2162: } else {
2163: throw new AssertionError(
2164: "shouldn't be parsing a validate expression");
2165: }
2166: foundCurly = true;
2167: }
2168:
2169: if (!foundCurly) {
2170: expect(Token.LCURLY);
2171: }
2172: nextToken();
2173:
2174: Expression exp = parseExpression();
2175: if (exp instanceof ElementCreator) {
2176: ((ElementCreator) exp).setValidationMode(mode);
2177: } else if (exp instanceof DocumentInstr) {
2178: ((DocumentInstr) exp).setValidationAction(mode);
2179: } else {
2180: // the expression must return a single element or document node. The type-
2181: // checking machinery can't handle a union type, so we just check that it's
2182: // a node for now. Because we are reusing XSLT copy-of code, we need
2183: // an ad-hoc check that the node is of the right kind.
2184: try {
2185: RoleLocator role = new RoleLocator(RoleLocator.TYPE_OP,
2186: "validate", 0, null);
2187: role.setSourceLocator(makeLocator());
2188: exp = TypeChecker.staticTypeCheck(exp,
2189: SequenceType.SINGLE_NODE, false, role, env);
2190: } catch (XPathException err) {
2191: grumble(err.getMessage());
2192: }
2193: exp = new CopyOf(exp, true, mode, null, true);
2194: setLocation(exp);
2195: ((CopyOf) exp).setRequireDocumentOrElement(true);
2196: }
2197:
2198: expect(Token.RCURLY);
2199: t.lookAhead(); // always done manually after an RCURLY
2200: nextToken();
2201: return makeTracer(offset, exp, Location.VALIDATE_EXPRESSION, -1);
2202: }
2203:
2204: /**
2205: * Parse an Extension Expression.
2206: * Syntax: "(#" QName arbitrary-text "#)")+ "{" expr? "}"
2207: * Currenly Saxon does not recognize any extension expressions, so the pragma is ignored and
2208: * the following expression is returned. (We don't even check the QName)
2209: */
2210:
2211: protected Expression parseExtensionExpression() throws StaticError {
2212: SchemaType requiredType = null;
2213: String pragmaContents = t.currentTokenValue.trim();
2214: Tokenizer s = new Tokenizer();
2215: s.setState(Tokenizer.BARE_NAME_STATE); // don't recognize QNAME() as a function call
2216: s.tokenize(pragmaContents, 0, pragmaContents.length(), 1);
2217: if (s.currentToken == Token.NAME) {
2218: int nameCode = makeNameCode(s.currentTokenValue, false);
2219: if ((nameCode & NamePool.FP_MASK) == StandardNames.SAXON_VALIDATE_TYPE) {
2220: if (!env.getConfiguration().isSchemaAware(
2221: Configuration.XQUERY)) {
2222: grumble("To use saxon:validate-type, "
2223: + "you need the schema-aware processor from http://www.saxonica.com/");
2224: }
2225: s.next();
2226: if (s.currentToken != Token.NAME) {
2227: grumble("Schema type expected in saxon:validate-type pragma");
2228: }
2229: int typeCode = makeNameCode(s.currentTokenValue, true);
2230: requiredType = env.getConfiguration().getSchemaType(
2231: typeCode & NamePool.FP_MASK);
2232: if (requiredType == null) {
2233: grumble("Unknown schema type "
2234: + s.currentTokenValue);
2235: }
2236: s.next();
2237: if (s.currentToken != Token.EOF) {
2238: grumble("Unexpected characters after schema type in saxon:validate-type pragma");
2239: }
2240: } else if (env.getNamePool().getURI(nameCode) == NamespaceConstant.SAXON) {
2241: grumble("Unrecognized Saxon pragma "
2242: + s.currentTokenValue);
2243: }
2244: } else {
2245: grumble("Pragma must start with a valid QName");
2246: }
2247:
2248: nextToken();
2249: Expression expr;
2250: if (t.currentToken == Token.PRAGMA) {
2251: expr = parseExtensionExpression();
2252: } else {
2253: expect(Token.LCURLY);
2254: nextToken();
2255: if (t.currentToken == Token.RCURLY) {
2256: t.lookAhead(); // always done manually after an RCURLY
2257: nextToken();
2258: grumble("Unrecognized pragma, with no fallback expression");
2259: }
2260: expr = parseExpression();
2261: expect(Token.RCURLY);
2262: t.lookAhead(); // always done manually after an RCURLY
2263: nextToken();
2264: }
2265: if (requiredType == null) {
2266: return expr;
2267: } else if (expr instanceof ElementCreator) {
2268: ((ElementCreator) expr).setSchemaType(requiredType);
2269: return expr;
2270: } else if (expr instanceof ElementCreator) {
2271: ((ElementCreator) expr).setSchemaType(requiredType);
2272: return expr;
2273: } else {
2274: CopyOf copy = new CopyOf(expr, true, 0, requiredType, true);
2275: copy.setLocationId(env.getLocationMap().allocateLocationId(
2276: env.getSystemId(), t.getLineNumber()));
2277: return copy;
2278: }
2279: }
2280:
2281: /**
2282: * Parse a node constructor. This is allowed only in XQuery. This method handles
2283: * both the XML-like "direct" constructors, and the XQuery-based "computed"
2284: * constructors.
2285: *
2286: * @return an Expression for evaluating the parsed constructor
2287: * @throws net.sf.saxon.trans.StaticError in the event of a syntax error.
2288: */
2289:
2290: protected Expression parseConstructor() throws StaticError {
2291: int offset = t.currentTokenStartOffset;
2292: switch (t.currentToken) {
2293: case Token.TAG:
2294: Expression tag = parsePseudoXML(false);
2295: lookAhead();
2296: t.setState(Tokenizer.OPERATOR_STATE);
2297: nextToken();
2298: return tag;
2299: case Token.KEYWORD_CURLY:
2300: String nodeKind = t.currentTokenValue;
2301: if (nodeKind == "validate") {
2302: return parseValidateExpression();
2303: } else if (nodeKind == "ordered" || nodeKind == "unordered") {
2304: // these are currently no-ops in Saxon
2305: nextToken();
2306: Expression content = parseExpression();
2307: expect(Token.RCURLY);
2308: lookAhead(); // must be done manually after an RCURLY
2309: nextToken();
2310: return content;
2311: } else if (nodeKind == "document") {
2312: nextToken();
2313: Expression content = parseExpression();
2314: expect(Token.RCURLY);
2315: lookAhead(); // must be done manually after an RCURLY
2316: nextToken();
2317: DocumentInstr doc = new DocumentInstr(false, null, env
2318: .getBaseURI());
2319: if (!((StaticQueryContext) env).isPreserveNamespaces()) {
2320: content = new CopyOf(content, false,
2321: Validation.PRESERVE, null, true);
2322: }
2323: doc.setValidationAction(((StaticQueryContext) env)
2324: .getConstructionMode());
2325: doc.setContentExpression(content);
2326: setLocation(doc, offset);
2327: return doc;
2328:
2329: } else if ("element".equals(nodeKind)) {
2330: nextToken();
2331: // get the expression that yields the element name
2332: Expression name = parseExpression();
2333: expect(Token.RCURLY);
2334: lookAhead(); // must be done manually after an RCURLY
2335: nextToken();
2336: expect(Token.LCURLY);
2337: t.setState(Tokenizer.DEFAULT_STATE);
2338: nextToken();
2339: Expression content = null;
2340: if (t.currentToken != Token.RCURLY) {
2341: // get the expression that yields the element content
2342: content = parseExpression();
2343: // if the child expression creates another element,
2344: // suppress validation, as the parent already takes care of it
2345: if (content instanceof ElementCreator) {
2346: ((ElementCreator) content)
2347: .setValidationMode(Validation.PRESERVE);
2348: }
2349: expect(Token.RCURLY);
2350: }
2351: lookAhead(); // done manually after an RCURLY
2352: nextToken();
2353:
2354: Instruction inst;
2355: if (name instanceof Value) {
2356: // if element name is supplied as a literal, treat it like a direct element constructor
2357: int nameCode;
2358: if (name instanceof StringValue
2359: && !(name instanceof AnyURIValue)) {
2360: String lex = ((StringValue) name)
2361: .getStringValue();
2362: // prevalidate the name before calling makeNameCode, because we want different
2363: // error behaviour
2364: try {
2365: nameChecker.getQNameParts(lex);
2366: } catch (QNameException err) {
2367: DynamicError e = new DynamicError(
2368: "Invalid QName in element constructor: "
2369: + lex);
2370: e.setErrorCode("XQDY0074");
2371: return new ErrorExpression(e);
2372: }
2373: try {
2374: nameCode = makeNameCode(lex, true);
2375: } catch (StaticError staticError) {
2376: String code = staticError
2377: .getErrorCodeLocalPart();
2378: if ("XPST0008".equals(code)
2379: || "XPST0081".equals(code)) {
2380: staticError.setErrorCode("XQDY0074");
2381: }
2382: throw staticError;
2383: }
2384: } else if (name instanceof QNameValue) {
2385: nameCode = env.getNamePool().allocate("",
2386: ((QNameValue) name).getNamespaceURI(),
2387: ((QNameValue) name).getLocalName());
2388: } else {
2389: grumble(
2390: "Element name must be either a string or a QName",
2391: "XPTY0004");
2392: return null;
2393: }
2394: inst = new FixedElement(nameCode,
2395: ((StaticQueryContext) env)
2396: .getActiveNamespaceCodes(),
2397: ((StaticQueryContext) env)
2398: .isInheritNamespaces(), null,
2399: ((StaticQueryContext) env)
2400: .getConstructionMode());
2401: if (content == null) {
2402: content = EmptySequence.getInstance();
2403: }
2404: ((FixedElement) inst).setContentExpression(content);
2405: setLocation(inst, offset);
2406: //makeContentConstructor(content, (InstructionWithChildren) inst, offset);
2407: return makeTracer(offset, inst,
2408: Location.LITERAL_RESULT_ELEMENT, nameCode);
2409: } else {
2410: // it really is a computed element constructor: save the namespace context
2411: inst = new ComputedElement(name, null, env
2412: .getNamespaceResolver(), null,
2413: ((StaticQueryContext) env)
2414: .getConstructionMode(),
2415: ((StaticQueryContext) env)
2416: .isInheritNamespaces(), true);
2417: setLocation(inst);
2418: if (content == null) {
2419: content = EmptySequence.getInstance();
2420: }
2421: ((ComputedElement) inst)
2422: .setContentExpression(content);
2423: setLocation(inst, offset);
2424: //makeContentConstructor(content, (InstructionWithChildren) inst, offset);
2425: return makeTracer(offset, inst,
2426: StandardNames.XSL_ELEMENT, -1);
2427: }
2428:
2429: } else if ("attribute".equals(nodeKind)) {
2430: nextToken();
2431: Expression name = parseExpression();
2432: expect(Token.RCURLY);
2433: lookAhead(); // must be done manually after an RCURLY
2434: nextToken();
2435: expect(Token.LCURLY);
2436: t.setState(Tokenizer.DEFAULT_STATE);
2437: nextToken();
2438: Expression content = null;
2439: if (t.currentToken != Token.RCURLY) {
2440: content = parseExpression();
2441: expect(Token.RCURLY);
2442: }
2443: lookAhead(); // after an RCURLY
2444: nextToken();
2445: if (name instanceof StringValue
2446: && !(name instanceof AnyURIValue)
2447: && content instanceof Value) {
2448: String attName = ((StringValue) name)
2449: .getStringValue();
2450: if (attName.equals("xmlns")
2451: || attName.startsWith("xmlns:")) {
2452: grumble(
2453: "Cannot create a namespace using an attribute constructor",
2454: "XQDY0044");
2455: }
2456: int nameCode;
2457: String lex = ((StringValue) name).getStringValue();
2458: // prevalidate the name before calling makeNameCode, because we want different
2459: // error behaviour
2460: try {
2461: nameChecker.getQNameParts(lex);
2462: } catch (QNameException err) {
2463: DynamicError e = new DynamicError(
2464: "Invalid QName in attribute constructor: "
2465: + lex);
2466: e.setErrorCode("XQDY0074");
2467: return new ErrorExpression(e);
2468: }
2469: try {
2470: nameCode = makeNameCode(attName, false);
2471: } catch (StaticError staticError) {
2472: String code = staticError
2473: .getErrorCodeLocalPart();
2474: if ("XPST0008".equals(code)
2475: || "XPST0081".equals(code)) {
2476: staticError.setErrorCode("XQDY0074");
2477: }
2478: throw staticError;
2479: }
2480: FixedAttribute fatt = new FixedAttribute(nameCode,
2481: Validation.STRIP, null,
2482: StandardNames.XDT_UNTYPED_ATOMIC);
2483: fatt.setRejectDuplicates();
2484: makeSimpleContent(content, fatt, offset);
2485: return makeTracer(offset, fatt,
2486: StandardNames.XSL_ATTRIBUTE, -1);
2487: } else {
2488: Attribute att = new Attribute(name, null, env
2489: .getNamespaceResolver(), Validation.STRIP,
2490: null, -1, true);
2491: att.setRejectDuplicates();
2492: makeSimpleContent(content, att, offset);
2493: return makeTracer(offset, att,
2494: StandardNames.XSL_ATTRIBUTE, -1);
2495: }
2496:
2497: } else if ("text".equals(nodeKind)) {
2498: nextToken();
2499: if (t.currentToken == Token.RCURLY) {
2500: lookAhead(); // after an RCURLY
2501: nextToken();
2502: return EmptySequence.getInstance();
2503: } else {
2504: Expression value = parseExpression();
2505: expect(Token.RCURLY);
2506: lookAhead(); // after an RCURLY
2507: nextToken();
2508: Expression select;
2509: try {
2510: select = stringify(value, true);
2511: } catch (XPathException e) {
2512: throw StaticError.makeStaticError(e);
2513: }
2514: ValueOf vof = new ValueOf(select, false, true);
2515: setLocation(vof, offset);
2516: return makeTracer(offset, vof,
2517: StandardNames.XSL_TEXT, -1);
2518: }
2519: } else if ("comment".equals(nodeKind)) {
2520: nextToken();
2521: if (t.currentToken == Token.RCURLY) {
2522: lookAhead(); // after an RCURLY
2523: nextToken();
2524: return EmptySequence.getInstance();
2525: } else {
2526: Expression value = parseExpression();
2527: expect(Token.RCURLY);
2528: lookAhead(); // after an RCURLY
2529: nextToken();
2530: Comment com = new Comment();
2531: makeSimpleContent(value, com, offset);
2532: return makeTracer(offset, com,
2533: StandardNames.XSL_COMMENT, -1);
2534: }
2535: } else if ("processing-instruction".equals(nodeKind)) {
2536: nextToken();
2537: Expression name = parseExpression();
2538: expect(Token.RCURLY);
2539: lookAhead(); // must be done manually after an RCURLY
2540: nextToken();
2541: expect(Token.LCURLY);
2542: t.setState(Tokenizer.DEFAULT_STATE);
2543: nextToken();
2544: Expression content = null;
2545: if (t.currentToken != Token.RCURLY) {
2546: content = parseExpression();
2547: expect(Token.RCURLY);
2548: }
2549: lookAhead(); // after an RCURLY
2550: nextToken();
2551: ProcessingInstruction pi = new ProcessingInstruction(
2552: name);
2553: makeSimpleContent(content, pi, offset);
2554: return makeTracer(offset, pi,
2555: StandardNames.XSL_PROCESSING_INSTRUCTION, -1);
2556:
2557: } else {
2558: grumble("Unrecognized node constructor "
2559: + t.currentTokenValue + "{}");
2560:
2561: }
2562: case Token.ELEMENT_QNAME:
2563: int nameCode = makeNameCode(t.currentTokenValue, true);
2564: Expression content = null;
2565: nextToken();
2566: if (t.currentToken != Token.RCURLY) {
2567: content = parseExpression();
2568: expect(Token.RCURLY);
2569: }
2570: lookAhead(); // after an RCURLY
2571: nextToken();
2572: FixedElement el2 = new FixedElement(nameCode,
2573: ((StaticQueryContext) env)
2574: .getActiveNamespaceCodes(),
2575: ((StaticQueryContext) env).isInheritNamespaces(),
2576: null, ((StaticQueryContext) env)
2577: .getConstructionMode());
2578: setLocation(el2, offset);
2579: if (content == null) {
2580: content = EmptySequence.getInstance();
2581: }
2582: el2.setContentExpression(content);
2583: //makeContentConstructor(content, el2, offset);
2584: return makeTracer(offset, el2,
2585: Location.LITERAL_RESULT_ELEMENT, nameCode);
2586: case Token.ATTRIBUTE_QNAME:
2587: if (t.currentTokenValue.equals("xmlns")
2588: || t.currentTokenValue.startsWith("xmlns:")) {
2589: grumble(
2590: "Cannot create a namespace using an attribute constructor",
2591: "XQDY0044");
2592: }
2593: int attNameCode = makeNameCode(t.currentTokenValue, false);
2594: Expression attContent = null;
2595: nextToken();
2596: if (t.currentToken != Token.RCURLY) {
2597: attContent = parseExpression();
2598: expect(Token.RCURLY);
2599: }
2600: lookAhead(); // after an RCURLY
2601: nextToken();
2602: FixedAttribute att2 = new FixedAttribute(attNameCode,
2603: Validation.STRIP, null,
2604: StandardNames.XDT_UNTYPED_ATOMIC);
2605: att2.setRejectDuplicates();
2606: makeSimpleContent(attContent, att2, offset);
2607: return makeTracer(offset, att2,
2608: Location.LITERAL_RESULT_ATTRIBUTE, attNameCode);
2609: case Token.PI_QNAME:
2610: Expression piName = new StringValue(t.currentTokenValue);
2611: Expression piContent = null;
2612: nextToken();
2613: if (t.currentToken != Token.RCURLY) {
2614: piContent = parseExpression();
2615: expect(Token.RCURLY);
2616: }
2617: lookAhead(); // after an RCURLY
2618: nextToken();
2619: ProcessingInstruction pi2 = new ProcessingInstruction(
2620: piName);
2621: makeSimpleContent(piContent, pi2, offset);
2622: return makeTracer(offset, pi2,
2623: StandardNames.XSL_PROCESSING_INSTRUCTION, -1);
2624: }
2625: return null;
2626: }
2627:
2628: /**
2629: * Make the instructions for the children of a node with simple content (attribute, text, PI, etc)
2630: *
2631: * @param content
2632: * @param inst
2633: * @param offset
2634: */
2635:
2636: private void makeSimpleContent(Expression content,
2637: SimpleNodeConstructor inst, int offset) throws StaticError {
2638: try {
2639: if (content == null) {
2640: inst.setSelect(StringValue.EMPTY_STRING);
2641: } else {
2642: inst.setSelect(stringify(content, false));
2643: }
2644: setLocation(inst, offset);
2645: } catch (XPathException e) {
2646: grumble(e.getMessage());
2647: }
2648: }
2649:
2650: /**
2651: * Make a sequence of instructions as the children of an element-construction instruction.
2652: * The idea here is to convert an XPath expression that "pulls" the content into a sequence
2653: * of XSLT-style instructions that push nodes directly onto the subtree, thus reducing the
2654: * need for copying of intermediate nodes.
2655: * @param content The content of the element as an expression
2656: * @param inst The element-construction instruction (Element or FixedElement)
2657: * @param offset the character position in the query
2658: */
2659: // private void makeContentConstructor(Expression content, InstructionWithChildren inst, int offset) {
2660: // if (content == null) {
2661: // inst.setChildren(null);
2662: // } else if (content instanceof AppendExpression) {
2663: // List instructions = new ArrayList(10);
2664: // convertAppendExpression((AppendExpression) content, instructions);
2665: // inst.setChildren((Expression[]) instructions.toArray(new Expression[instructions.size()]));
2666: // } else {
2667: // Expression children[] = {content};
2668: // inst.setChildren(children);
2669: // }
2670: // setLocation(inst, offset);
2671: // }
2672: /**
2673: * Parse pseudo-XML syntax in direct element constructors, comments, CDATA, etc.
2674: * This is handled by reading single characters from the Tokenizer until the
2675: * end of the tag (or an enclosed expression) is enountered.
2676: * This method is also used to read an end tag. Because an end tag is not an
2677: * expression, the method in this case returns a StringValue containing the
2678: * contents of the end tag.
2679: *
2680: * @param allowEndTag true if the context allows an End Tag to appear here
2681: * @return an Expression representing the result of parsing the constructor.
2682: * If an end tag was read, its contents will be returned as a StringValue.
2683: */
2684:
2685: private Expression parsePseudoXML(boolean allowEndTag)
2686: throws StaticError {
2687: Expression exp = null;
2688: int offset = t.inputOffset;
2689: // we're reading raw characters, so we don't want the currentTokenStartOffset
2690: char c = t.nextChar();
2691: switch (c) {
2692: case '!':
2693: c = t.nextChar();
2694: if (c == '-') {
2695: exp = parseCommentConstructor();
2696: } else if (c == '[') {
2697: grumble("A CDATA section is allowed only in element content");
2698: // if CDATA were allowed here, we would have already read it
2699: } else {
2700: grumble("Expected '--' or '[CDATA[' after '<!'");
2701: }
2702: break;
2703: case '?':
2704: exp = parsePIConstructor();
2705: break;
2706: case '/':
2707: if (allowEndTag) {
2708: FastStringBuffer sb = new FastStringBuffer(40);
2709: while (true) {
2710: c = t.nextChar();
2711: if (c == '>') {
2712: break;
2713: }
2714: sb.append(c);
2715: }
2716: return new StringValue(sb);
2717: }
2718: grumble("Unmatched XML end tag");
2719: break;
2720: default:
2721: t.unreadChar();
2722: exp = parseDirectElementConstructor();
2723: }
2724: setLocation(exp, offset);
2725: return exp;
2726: }
2727:
2728: /**
2729: * Parse a direct element constructor
2730: *
2731: * @return the expression representing the constructor
2732: * @throws StaticError
2733: */
2734:
2735: private Expression parseDirectElementConstructor()
2736: throws StaticError {
2737: int offset = t.inputOffset - 1;
2738: // we're reading raw characters, so we don't want the currentTokenStartOffset
2739: char c;
2740: FastStringBuffer buff = new FastStringBuffer(40);
2741: int namespaceCount = 0;
2742: while (true) {
2743: c = t.nextChar();
2744: if (c == ' ' || c == '\n' || c == '\r' || c == '\t'
2745: || c == '/' || c == '>') {
2746: break;
2747: }
2748: buff.append(c);
2749: }
2750: String elname = buff.toString();
2751: HashMap attributes = new HashMap(10);
2752: while (true) {
2753: // loop through the attributes
2754: // We must process namespace declaration attributes first;
2755: // their scope applies to all preceding attribute names and values.
2756: // But finding the delimiting quote of an attribute value requires the
2757: // XPath expressions to be parsed, because they may contain nested quotes.
2758: // So we parse in "scanOnly" mode, which ignores any undeclared namespace
2759: // prefixes, use the result of this parse to determine the length of the
2760: // attribute value, save the value, and reparse it when all the namespace
2761: // declarations have been dealt with.
2762: c = skipSpaces(c);
2763: if (c == '/' || c == '>') {
2764: break;
2765: }
2766: int attOffset = t.inputOffset - 1;
2767: buff.setLength(0);
2768: // read the attribute name
2769: while (true) {
2770: buff.append(c);
2771: c = t.nextChar();
2772: if (c == ' ' || c == '\n' || c == '\r' || c == '\t'
2773: || c == '=') {
2774: break;
2775: }
2776: }
2777: String attName = buff.toString();
2778: if (!nameChecker.isQName(attName)) {
2779: grumble("Invalid attribute name "
2780: + Err.wrap(attName, Err.ATTRIBUTE));
2781: }
2782: c = skipSpaces(c);
2783: expectChar(c, '=');
2784: c = t.nextChar();
2785: c = skipSpaces(c);
2786:
2787: Expression avt;
2788: try {
2789: avt = makeAttributeContent(t.input, t.inputOffset, c,
2790: true);
2791: } catch (StaticError err) {
2792: grumble(err.getMessage());
2793: return null;
2794: } catch (XPathException err) {
2795: throw err.makeStatic();
2796: }
2797: // by convention, this returns the end position when called with scanOnly set
2798: int end = (int) ((IntegerValue) avt).longValue();
2799: // save the value with its surrounding quotes
2800: String val = t.input.substring(t.inputOffset - 1, end + 1);
2801: // and without
2802: String rval = t.input.substring(t.inputOffset, end);
2803: t.inputOffset = end + 1;
2804: // on return, the current character is the closing quote
2805: c = t.nextChar();
2806: if (!(c == ' ' || c == '\n' || c == '\r' || c == '\t'
2807: || c == '/' || c == '>')) {
2808: grumble("There must be whitespace after every attribute except the last");
2809: }
2810: if ("xmlns".equals(attName) || attName.startsWith("xmlns:")) {
2811: if (rval.indexOf('{') >= 0) {
2812: grumble("Namespace URI must be a constant value",
2813: "XQST0022");
2814: }
2815: String prefix, uri;
2816: if ("xmlns".equals(attName)) {
2817: prefix = "";
2818: uri = rval;
2819: } else {
2820: prefix = attName.substring(6);
2821: if (prefix.equals("xml")) {
2822: grumble("Cannot redeclare the XML namespace",
2823: "XQST0070");
2824: } else if (prefix.equals("xmlns")) {
2825: grumble(
2826: "Cannot use xmlns as a namespace prefix",
2827: "XQST0070");
2828: }
2829: uri = rval;
2830: if ("".equals(uri)) {
2831: grumble("Namespace URI must not be empty",
2832: "XQST0085");
2833: }
2834: }
2835: namespaceCount++;
2836: ((StaticQueryContext) env).declareActiveNamespace(
2837: prefix, uri);
2838: }
2839: if (attributes.get(attName) != null) {
2840: if ("xmlns".equals(attName)
2841: || attName.startsWith("xmlns:")) {
2842: grumble("Duplicate namespace declaration "
2843: + attName, "XQST0071");
2844: } else {
2845: grumble("Duplicate attribute name " + attName,
2846: "XQST0040");
2847: }
2848: }
2849: if (attName.equals("xml:id")
2850: && !nameChecker.isValidNCName(rval)) {
2851: grumble("Value of xml:id must be a valid NCName",
2852: "XQST0082");
2853: }
2854: AttributeDetails a = new AttributeDetails();
2855: a.value = val;
2856: a.startOffset = attOffset;
2857: attributes.put(attName, a);
2858: }
2859: String namespace;
2860: int elNameCode = 0;
2861: try {
2862: String[] parts = nameChecker.getQNameParts(elname);
2863: namespace = ((StaticQueryContext) env)
2864: .checkURIForPrefix(parts[0]);
2865: if (namespace == null) {
2866: grumble("Undeclared prefix in element name "
2867: + Err.wrap(elname, Err.ELEMENT), "XPST0008");
2868: }
2869: elNameCode = env.getNamePool().allocate(parts[0],
2870: namespace, parts[1]);
2871: } catch (QNameException e) {
2872: grumble("Invalid element name "
2873: + Err.wrap(elname, Err.ELEMENT));
2874: }
2875: int validationMode = ((StaticQueryContext) env)
2876: .getConstructionMode();
2877: FixedElement elInst = new FixedElement(elNameCode,
2878: ((StaticQueryContext) env).getActiveNamespaceCodes(),
2879: ((StaticQueryContext) env).isInheritNamespaces(), null,
2880: validationMode);
2881:
2882: setLocation(elInst, offset);
2883:
2884: List contents = new ArrayList(10);
2885:
2886: IntHashSet attFingerprints = new IntHashSet(attributes.size());
2887: // we've checked for duplicate lexical QNames, but not for duplicate expanded-QNames
2888: for (Iterator iter = attributes.keySet().iterator(); iter
2889: .hasNext();) {
2890: String attName = (String) iter.next();
2891: AttributeDetails a = (AttributeDetails) attributes
2892: .get(attName);
2893: String attValue = a.value;
2894: int attOffset = a.startOffset;
2895:
2896: if ("xmlns".equals(attName) || attName.startsWith("xmlns:")) {
2897: // do nothing
2898: } else if (scanOnly) {
2899: // This means we are prescanning an attribute constructor, and we found a nested attribute
2900: // constructor, which we have prescanned; we now don't want to re-process the nested attribute
2901: // constructor because it might depend on things like variables declared in the containing
2902: // attribute constructor, and in any case we're going to come back to it again later.
2903: // See test qxmp180
2904: } else {
2905: int attNameCode = 0;
2906: String attNamespace;
2907: try {
2908: String[] parts = nameChecker.getQNameParts(attName);
2909: if ("".equals(parts[0])) {
2910: // attributes don't use the default namespace
2911: attNamespace = "";
2912: } else {
2913: attNamespace = ((StaticQueryContext) env)
2914: .checkURIForPrefix(parts[0]);
2915: }
2916: if (attNamespace == null) {
2917: grumble("Undeclared prefix in attribute name "
2918: + Err.wrap(attName, Err.ATTRIBUTE),
2919: "XPST0008");
2920: }
2921: attNameCode = env.getNamePool().allocate(parts[0],
2922: attNamespace, parts[1]);
2923: int key = (attNameCode & NamePool.FP_MASK);
2924: if (attFingerprints.contains(key)) {
2925: grumble("Duplicate expanded attribute name "
2926: + attName, "XQST0040");
2927: }
2928: attFingerprints.add(key);
2929: } catch (QNameException e) {
2930: grumble("Invalid attribute name "
2931: + Err.wrap(attName, Err.ATTRIBUTE));
2932: }
2933:
2934: FixedAttribute attInst = new FixedAttribute(
2935: attNameCode, Validation.STRIP, null,
2936: StandardNames.XDT_UNTYPED_ATOMIC);
2937:
2938: setLocation(attInst);
2939: Expression select;
2940: try {
2941: select = makeAttributeContent(attValue, 1, attValue
2942: .charAt(0), false);
2943: } catch (XPathException err) {
2944: throw err.makeStatic();
2945: }
2946: attInst.setSelect(select);
2947: attInst.setRejectDuplicates();
2948: setLocation(attInst);
2949: contents
2950: .add(makeTracer(attOffset, attInst,
2951: Location.LITERAL_RESULT_ATTRIBUTE,
2952: attNameCode));
2953: }
2954: }
2955: if (c == '/') {
2956: // empty element tag
2957: expectChar(t.nextChar(), '>');
2958: } else {
2959: readElementContent(elname, contents);
2960: }
2961:
2962: Expression[] elk = new Expression[contents.size()];
2963: for (int i = 0; i < contents.size(); i++) {
2964: // if the child expression creates another element,
2965: // suppress validation, as the parent already takes care of it
2966: if (validationMode != Validation.STRIP
2967: && contents.get(i) instanceof ComputedExpression) {
2968: ((ComputedExpression) contents.get(i))
2969: .suppressValidation(validationMode);
2970: }
2971: elk[i] = (Expression) contents.get(i);
2972: }
2973: Block block = new Block();
2974: block.setChildren(elk);
2975: elInst.setContentExpression(block);
2976:
2977: // reset the in-scope namespaces to what they were before
2978:
2979: for (int n = 0; n < namespaceCount; n++) {
2980: ((StaticQueryContext) env).undeclareNamespace();
2981: }
2982:
2983: return makeTracer(offset, elInst,
2984: Location.LITERAL_RESULT_ELEMENT, elNameCode);
2985: }
2986:
2987: /**
2988: * Parse the content of an attribute in a direct element constructor. This may contain nested expressions
2989: * within curly braces. A particular problem is that the namespaces used in the expression may not yet be
2990: * known. This means we need the ability to parse in "scanOnly" mode, where undeclared namespace prefixes
2991: * are ignored.
2992: * <p/>
2993: * The code is based on the XSLT code in {@link AttributeValueTemplate#make}: the main difference is that
2994: * character entities and built-in entity references need to be recognized and expanded. Also, whitespace
2995: * needs to be normalized, mimicking the action of an XML parser
2996: */
2997:
2998: private Expression makeAttributeContent(String avt, int start,
2999: char terminator, boolean scanOnly) throws XPathException {
3000:
3001: int lineNumber = t.getLineNumber();
3002: List components = new ArrayList(10);
3003:
3004: int i0, i1, i2, i8, i9, len, last;
3005: last = start;
3006: len = avt.length();
3007: while (last < len) {
3008: i2 = avt.indexOf(terminator, last);
3009: if (i2 < 0) {
3010: throw new StaticError(
3011: "Attribute constructor is not properly terminated");
3012: }
3013:
3014: i0 = avt.indexOf("{", last);
3015: i1 = avt.indexOf("{{", last);
3016: i8 = avt.indexOf("}", last);
3017: i9 = avt.indexOf("}}", last);
3018:
3019: if ((i0 < 0 || i2 < i0) && (i8 < 0 || i2 < i8)) { // found end of string
3020: addStringComponent(components, avt, last, i2);
3021:
3022: // look for doubled quotes, and skip them (for now)
3023: if (i2 + 1 < avt.length()
3024: && avt.charAt(i2 + 1) == terminator) {
3025: components.add(new StringValue(terminator + ""));
3026: last = i2 + 2;
3027: continue;
3028: } else {
3029: last = i2;
3030: break;
3031: }
3032: } else if (i8 >= 0 && (i0 < 0 || i8 < i0)) { // found a "}"
3033: if (i8 != i9) { // a "}" that isn't a "}}"
3034: throw new StaticError(
3035: "Closing curly brace in attribute value template \""
3036: + avt + "\" must be doubled");
3037: }
3038: addStringComponent(components, avt, last, i8 + 1);
3039: last = i8 + 2;
3040: } else if (i1 >= 0 && i1 == i0) { // found a doubled "{{"
3041: addStringComponent(components, avt, last, i1 + 1);
3042: last = i1 + 2;
3043: } else if (i0 >= 0) { // found a single "{"
3044: if (i0 > last) {
3045: addStringComponent(components, avt, last, i0);
3046: }
3047: Expression exp;
3048: ExpressionParser parser;
3049: parser = new QueryParser();
3050: parser.setScanOnly(scanOnly);
3051: if (rangeVariables != null) {
3052: parser.setRangeVariableStack(rangeVariables);
3053: }
3054: exp = parser.parse(avt, i0 + 1, Token.RCURLY,
3055: lineNumber, env);
3056: if (!scanOnly) {
3057: exp = exp.simplify(env);
3058: }
3059: last = parser.getTokenizer().currentTokenStartOffset + 1;
3060: components.add(makeStringJoin(exp, env));
3061:
3062: } else {
3063: throw new IllegalStateException(
3064: "Internal error parsing direct attribute constructor");
3065: }
3066: }
3067:
3068: // if this is simply a prescan, return the position of the end of the
3069: // AVT, so we can parse it properly later
3070:
3071: if (scanOnly) {
3072: return new IntegerValue(last);
3073: }
3074:
3075: // is it empty?
3076:
3077: if (components.size() == 0) {
3078: return StringValue.EMPTY_STRING;
3079: }
3080:
3081: // is it a single component?
3082:
3083: if (components.size() == 1) {
3084: return ((Expression) components.get(0)).simplify(env);
3085: }
3086:
3087: // otherwise, return an expression that concatenates the components
3088:
3089: Concat fn = (Concat) SystemFunction.makeSystemFunction(
3090: "concat", components.size(), env.getNamePool());
3091: Expression[] args = new Expression[components.size()];
3092: components.toArray(args);
3093: fn.setArguments(args);
3094: fn.setLocationId(env.getLocationMap().allocateLocationId(
3095: env.getSystemId(), lineNumber));
3096: return fn.simplify(env);
3097:
3098: }
3099:
3100: private void addStringComponent(List components, String avt,
3101: int start, int end) throws XPathException {
3102: // analyze fixed text within the value of a direct attribute constructor.
3103: if (start < end) {
3104: FastStringBuffer sb = new FastStringBuffer(end - start);
3105: for (int i = start; i < end; i++) {
3106: char c = avt.charAt(i);
3107: switch (c) {
3108: case '&': {
3109: int semic = avt.indexOf(';', i);
3110: if (semic < 0) {
3111: grumble("No closing ';' found for entity or character reference");
3112: } else {
3113: String entity = avt.substring(i + 1, semic);
3114: sb.append(analyzeEntityReference(entity));
3115: i = semic;
3116: }
3117: break;
3118: }
3119: case '<':
3120: grumble("The < character must not appear in attribute content");
3121: break;
3122: case '\n':
3123: case '\t':
3124: sb.append(' ');
3125: break;
3126: case '\r':
3127: sb.append(' ');
3128: if (i + 1 < end && avt.charAt(i + 1) == '\n') {
3129: i++;
3130: }
3131: break;
3132: default:
3133: sb.append(c);
3134:
3135: }
3136: }
3137: components.add(StringValue.makeStringValue(sb));
3138: }
3139: }
3140:
3141: /**
3142: * Read the content of a direct element constructor
3143: *
3144: * @param startTag the element start tag
3145: * @param components an empty list, to which the expressions comprising the element contents are added
3146: * @throws StaticError if any static errors are detected
3147: */
3148: private void readElementContent(String startTag, List components)
3149: throws StaticError {
3150: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
3151: try {
3152: boolean afterEnclosedExpr = false;
3153: while (true) {
3154: // read all the components of the element value
3155: FastStringBuffer text = new FastStringBuffer(256);
3156: char c;
3157: boolean containsEntities = false;
3158: while (true) {
3159: c = t.nextChar();
3160: if (c == '<') {
3161: // See if we've got a CDATA section
3162: if (t.nextChar() == '!') {
3163: if (t.nextChar() == '[') {
3164: readCDATASection(text);
3165: containsEntities = true;
3166: continue;
3167: } else {
3168: t.unreadChar();
3169: t.unreadChar();
3170: }
3171: } else {
3172: t.unreadChar();
3173: }
3174: break;
3175: } else if (c == '&') {
3176: text.append(readEntityReference());
3177: containsEntities = true;
3178: } else if (c == '}') {
3179: c = t.nextChar();
3180: if (c != '}') {
3181: grumble("'}' must be written as '}}' within element content");
3182: }
3183: text.append(c);
3184: } else if (c == '{') {
3185: c = t.nextChar();
3186: if (c != '{') {
3187: c = '{';
3188: break;
3189: }
3190: text.append(c);
3191: } else {
3192: text.append(c);
3193: }
3194: }
3195: if (text.length() > 0
3196: && (containsEntities | preserveSpace | !Whitespace
3197: .isWhite(text))) {
3198: ValueOf inst = new ValueOf(new StringValue(text
3199: .condense()), false, false);
3200: setLocation(inst);
3201: components.add(inst);
3202: afterEnclosedExpr = false;
3203: }
3204: if (c == '<') {
3205: Expression exp = parsePseudoXML(true);
3206: // An end tag can appear here, and is returned as a string value
3207: if (exp instanceof StringValue) {
3208: String endTag = ((StringValue) exp)
3209: .getStringValue().trim();
3210: if (endTag.equals(startTag)) {
3211: return;
3212: } else {
3213: grumble("end tag </" + endTag
3214: + "> does not match start tag <"
3215: + startTag + '>');
3216: }
3217: } else {
3218: components.add(exp);
3219: }
3220: } else {
3221: // we read an '{' indicating an enclosed expression
3222: if (afterEnclosedExpr) {
3223: Expression previousComponent = (Expression) components
3224: .get(components.size() - 1);
3225: ItemType previousItemType = previousComponent
3226: .getItemType(th);
3227: if (!(previousItemType instanceof NodeTest)) {
3228: // Add a zero-length text node, to prevent {"a"}{"b"} generating an intervening space
3229: // See tests (qxmp132, qxmp261)
3230: ValueOf inst = new ValueOf(
3231: StringValue.EMPTY_STRING, false,
3232: false);
3233: setLocation(inst);
3234: components.add(inst);
3235: }
3236: }
3237: t.unreadChar();
3238: t.setState(Tokenizer.DEFAULT_STATE);
3239: lookAhead();
3240: nextToken();
3241: Expression exp = parseExpression();
3242: if (!((StaticQueryContext) env)
3243: .isPreserveNamespaces()) {
3244: exp = new CopyOf(exp, false,
3245: Validation.PRESERVE, null, true);
3246: }
3247: components.add(exp);
3248: expect(Token.RCURLY);
3249: afterEnclosedExpr = true;
3250: }
3251: }
3252: } catch (StringIndexOutOfBoundsException err) {
3253: grumble("No closing end tag found for direct element constructor");
3254: }
3255: }
3256:
3257: private Expression parsePIConstructor() throws StaticError {
3258: try {
3259: FastStringBuffer pi = new FastStringBuffer(120);
3260: int firstSpace = -1;
3261: while (!pi.toString().endsWith("?>")) {
3262: char c = t.nextChar();
3263: if (firstSpace < 0 && " \t\r\n".indexOf(c) >= 0) {
3264: firstSpace = pi.length();
3265: }
3266: pi.append(c);
3267: }
3268: pi.setLength(pi.length() - 2);
3269:
3270: String target;
3271: String data = "";
3272: if (firstSpace < 0) {
3273: // there is no data part
3274: target = pi.toString();
3275: } else {
3276: // trim leading space from the data part, but not trailing space
3277: target = pi.toString().substring(0, firstSpace);
3278: firstSpace++;
3279: while (firstSpace < pi.length()
3280: && " \t\r\n".indexOf(pi.charAt(firstSpace)) >= 0) {
3281: firstSpace++;
3282: }
3283: data = pi.toString().substring(firstSpace);
3284: }
3285:
3286: if (!nameChecker.isValidNCName(target)) {
3287: grumble("Invalid processing instruction name "
3288: + Err.wrap(target));
3289: }
3290:
3291: ProcessingInstruction instruction = new ProcessingInstruction(
3292: new StringValue(target));
3293: instruction.setSelect(StringValue.makeStringValue(data));
3294: setLocation(instruction);
3295: return instruction;
3296: } catch (StringIndexOutOfBoundsException err) {
3297: grumble("No closing '?>' found for processing instruction");
3298: return null;
3299: }
3300: }
3301:
3302: private void readCDATASection(FastStringBuffer cdata)
3303: throws StaticError {
3304: try {
3305: char c;
3306: // CDATA section
3307: c = t.nextChar();
3308: expectChar(c, 'C');
3309: c = t.nextChar();
3310: expectChar(c, 'D');
3311: c = t.nextChar();
3312: expectChar(c, 'A');
3313: c = t.nextChar();
3314: expectChar(c, 'T');
3315: c = t.nextChar();
3316: expectChar(c, 'A');
3317: c = t.nextChar();
3318: expectChar(c, '[');
3319: while (!cdata.toString().endsWith("]]>")) {
3320: cdata.append(t.nextChar());
3321: }
3322: cdata.setLength(cdata.length() - 3);
3323: } catch (StringIndexOutOfBoundsException err) {
3324: grumble("No closing ']]>' found for CDATA section");
3325: }
3326: }
3327:
3328: private Expression parseCommentConstructor() throws StaticError {
3329: try {
3330: char c = t.nextChar();
3331: ;
3332: // XML-like comment
3333: expectChar(c, '-');
3334: FastStringBuffer comment = new FastStringBuffer(240);
3335: while (!comment.toString().endsWith("--")) {
3336: comment.append(t.nextChar());
3337: }
3338: if (t.nextChar() != '>') {
3339: grumble("'--' is not permitted in an XML comment");
3340: }
3341: CharSequence commentText = comment.subSequence(0, comment
3342: .length() - 2);
3343: Comment instruction = new Comment();
3344: instruction.setSelect(new StringValue(commentText));
3345: setLocation(instruction);
3346: return instruction;
3347: } catch (StringIndexOutOfBoundsException err) {
3348: grumble("No closing '-->' found for comment constructor");
3349: return null;
3350: }
3351: }
3352:
3353: /**
3354: * Convert an expression so it generates a space-separated sequence of strings
3355: *
3356: * @param exp the expression that calculates the content
3357: * @param noNodeIfEmpty if true, no node is produced when the value of the content
3358: * expression is an empty sequence. If false, the effect of supplying an empty sequence
3359: * is that a node is created whose string-value is a zero-length string. Set to true for
3360: * text node constructors, false for other kinds of node.
3361: */
3362:
3363: private Expression stringify(Expression exp, boolean noNodeIfEmpty)
3364: throws XPathException {
3365: return new QuerySimpleContentConstructor(exp,
3366: StringValue.SINGLE_SPACE, noNodeIfEmpty).simplify(env);
3367: }
3368:
3369: /**
3370: * Method to make a string literal from a token identified as a string
3371: * literal. This is trivial in XPath, but in XQuery the method is overridden
3372: * to identify pseudo-XML character and entity references
3373: *
3374: * @param token
3375: * @return The string value of the string literal, after dereferencing entity and
3376: * character references
3377: */
3378:
3379: protected StringValue makeStringLiteral(String token)
3380: throws StaticError {
3381: if (token.indexOf('&') == -1) {
3382: return StringValue.makeStringValue(token);
3383: } else {
3384: FastStringBuffer sb = unescape(token);
3385: return StringValue.makeStringValue(sb);
3386: }
3387: }
3388:
3389: /**
3390: * Unescape character references and built-in entity references in a string
3391: *
3392: * @param token
3393: * @return
3394: * @throws StaticError
3395: */
3396:
3397: private FastStringBuffer unescape(String token) throws StaticError {
3398: FastStringBuffer sb = new FastStringBuffer(80);
3399: for (int i = 0; i < token.length(); i++) {
3400: char c = token.charAt(i);
3401: if (c == '&') {
3402: int semic = token.indexOf(';', i);
3403: if (semic < 0) {
3404: grumble("No closing ';' found for entity or character reference");
3405: } else {
3406: String entity = token.substring(i + 1, semic);
3407: sb.append(analyzeEntityReference(entity));
3408: i = semic;
3409: }
3410: } else {
3411: sb.append(c);
3412: }
3413: }
3414: return sb;
3415: }
3416:
3417: /**
3418: * Read a pseudo-XML character reference or entity reference.
3419: *
3420: * @return The character represented by the character or entity reference. Note
3421: * that this is a string rather than a char because a char only accommodates characters
3422: * up to 65535.
3423: * @throws net.sf.saxon.trans.StaticError if the character or entity reference is not well-formed
3424: */
3425:
3426: private String readEntityReference() throws StaticError {
3427: try {
3428: FastStringBuffer sb = new FastStringBuffer(40);
3429: while (true) {
3430: char c = t.nextChar();
3431: if (c == ';') {
3432: break;
3433: }
3434: sb.append(c);
3435: }
3436: String entity = sb.toString();
3437: return analyzeEntityReference(entity);
3438: } catch (StringIndexOutOfBoundsException err) {
3439: grumble("No closing ';' found for entity or character reference");
3440: }
3441: return null; // to keep the Java compiler happy
3442: }
3443:
3444: private String analyzeEntityReference(String entity)
3445: throws StaticError {
3446: if ("lt".equals(entity)) {
3447: return "<";
3448: } else if ("gt".equals(entity)) {
3449: return ">";
3450: } else if ("amp".equals(entity)) {
3451: return "&";
3452: } else if ("quot".equals(entity)) {
3453: return "\"";
3454: } else if ("apos".equals(entity)) {
3455: return "'";
3456: } else if (entity.length() < 2 || entity.charAt(0) != '#') {
3457: grumble("invalid entity reference &" + entity + ';');
3458: return null;
3459: } else {
3460: entity = entity.toLowerCase();
3461: return parseCharacterReference(entity);
3462: }
3463: }
3464:
3465: private String parseCharacterReference(String entity)
3466: throws StaticError {
3467: int value = 0;
3468: if (entity.charAt(1) == 'x') {
3469: for (int i = 2; i < entity.length(); i++) {
3470: int digit = "0123456789abcdef"
3471: .indexOf(entity.charAt(i));
3472: if (digit < 0) {
3473: grumble("invalid character '" + entity.charAt(i)
3474: + "' in hex character reference");
3475: }
3476: value = (value * 16) + digit;
3477: }
3478: } else {
3479: for (int i = 1; i < entity.length(); i++) {
3480: int digit = "0123456789".indexOf(entity.charAt(i));
3481: if (digit < 0) {
3482: grumble("invalid character '" + entity.charAt(i)
3483: + "' in decimal character reference");
3484: }
3485: value = (value * 10) + digit;
3486: }
3487: }
3488: // following code borrowed from AElfred
3489: // check for character refs being legal XML
3490: if ((value < 0x0020 && !(value == '\n' || value == '\t' || value == '\r'))
3491: || (value >= 0xD800 && value <= 0xDFFF)
3492: || value == 0xFFFE
3493: || value == 0xFFFF
3494: || value > 0x0010ffff) {
3495: grumble("Invalid XML character reference x"
3496: + Integer.toHexString(value));
3497: }
3498:
3499: // Check for surrogates: 00000000 0000xxxx yyyyyyyy zzzzzzzz
3500: // (1101|10xx|xxyy|yyyy + 1101|11yy|zzzz|zzzz:
3501: if (value <= 0x0000ffff) {
3502: // no surrogates needed
3503: return "" + (char) value;
3504: } else if (value <= 0x0010ffff) {
3505: value -= 0x10000;
3506: // > 16 bits, surrogate needed
3507: return "" + ((char) (0xd800 | (value >> 10)))
3508: + ((char) (0xdc00 | (value & 0x0003ff)));
3509: } else {
3510: // too big for surrogate
3511: grumble("Character reference x"
3512: + Integer.toHexString(value) + " is too large");
3513: }
3514: return null;
3515: }
3516:
3517: /**
3518: * Handle a URI literal. This is whitespace-normalized as well as being unescaped
3519: */
3520:
3521: private String URILiteral(String in) throws StaticError {
3522: return Whitespace.applyWhitespaceNormalization(
3523: Whitespace.COLLAPSE, unescape(in)).toString();
3524: }
3525:
3526: /**
3527: * Lookahead one token, catching any exception thrown by the tokenizer. This
3528: * method is only called from the query parser when switching from character-at-a-time
3529: * mode to tokenizing mode
3530: */
3531:
3532: private void lookAhead() throws StaticError {
3533: try {
3534: t.lookAhead();
3535: } catch (XPathException err) {
3536: grumble(err.getMessage());
3537: }
3538: }
3539:
3540: /**
3541: * Skip whitespace.
3542: *
3543: * @param c the current character
3544: * @return the first character after any whitespace
3545: */
3546:
3547: private char skipSpaces(char c) {
3548: while (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
3549: c = t.nextChar();
3550: }
3551: return c;
3552: }
3553:
3554: /**
3555: * Test whether the current character is the expected character.
3556: *
3557: * @param actual The character that was read
3558: * @param expected The character that was expected
3559: * @throws net.sf.saxon.trans.StaticError if they are different
3560: */
3561:
3562: private void expectChar(char actual, char expected)
3563: throws StaticError {
3564: if (actual != expected) {
3565: grumble("Expected '" + expected + "', found '" + actual
3566: + '\'');
3567: }
3568: }
3569:
3570: /**
3571: * Get the current language (XPath or XQuery)
3572: */
3573:
3574: protected String getLanguage() {
3575: return "XQuery";
3576: }
3577:
3578: private static class AttributeDetails {
3579: String value;
3580: int startOffset;
3581: }
3582:
3583: private static class Import {
3584: String namespaceURI;
3585: List locationURIs;
3586: }
3587: }
3588:
3589: //
3590: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
3591: // you may not use this file except in compliance with the License. You may obtain a copy of the
3592: // License at http://www.mozilla.org/MPL/
3593: //
3594: // Software distributed under the License is distributed on an "AS IS" basis,
3595: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
3596: // See the License for the specific language governing rights and limitations under the License.
3597: //
3598: // The Original Code is: all this file.
3599: //
3600: // The Initial Developer of the Original Code is Michael H. Kay
3601: //
3602: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
3603: //
3604: // Contributor(s): none.
3605: //
|