PageRenderTime 74ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/saxonB/net/sf/saxon/query/QueryParser.java

https://bitbucket.org/dmwelch/phdxnat_pipeline
Java | 3715 lines | 2775 code | 279 blank | 661 comment | 744 complexity | f0fe8568f3211e519f536f98bb54b80a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. package net.sf.saxon.query;
  2. import net.sf.saxon.Configuration;
  3. import net.sf.saxon.trans.Err;
  4. import net.sf.saxon.Platform;
  5. import net.sf.saxon.charcode.UTF16;
  6. import net.sf.saxon.event.PipelineConfiguration;
  7. import net.sf.saxon.expr.*;
  8. import net.sf.saxon.functions.*;
  9. import net.sf.saxon.instruct.*;
  10. import net.sf.saxon.om.*;
  11. import net.sf.saxon.pattern.NodeTest;
  12. import net.sf.saxon.sort.*;
  13. import net.sf.saxon.style.AttributeValueTemplate;
  14. import net.sf.saxon.sxpath.IndependentContext;
  15. import net.sf.saxon.trace.Location;
  16. import net.sf.saxon.trans.XPathException;
  17. import net.sf.saxon.type.*;
  18. import net.sf.saxon.value.*;
  19. import javax.xml.transform.OutputKeys;
  20. import javax.xml.transform.TransformerConfigurationException;
  21. import javax.xml.transform.stream.StreamSource;
  22. import java.net.URI;
  23. import java.net.URISyntaxException;
  24. import java.util.*;
  25. import java.util.regex.Pattern;
  26. import java.io.IOException;
  27. /**
  28. * This class defines extensions to the XPath parser to handle the additional
  29. * syntax supported in XQuery
  30. */
  31. public class QueryParser extends ExpressionParser {
  32. private boolean memoFunction = false;
  33. private boolean disableCycleChecks = false;
  34. private int errorCount = 0;
  35. private XPathException firstError = null;
  36. protected Executable executable;
  37. private boolean foundCopyNamespaces = false;
  38. private boolean foundBoundarySpaceDeclaration = false;
  39. private boolean foundOrderingDeclaration = false;
  40. private boolean foundEmptyOrderingDeclaration = false;
  41. private boolean foundDefaultCollation = false;
  42. private boolean foundConstructionDeclaration = false;
  43. private boolean foundDefaultFunctionNamespace = false;
  44. private boolean foundDefaultElementNamespace = false;
  45. private boolean foundBaseURIDeclaration = false;
  46. private boolean preambleProcessed = false;
  47. public Set importedModules = new HashSet(5);
  48. List namespacesToBeSealed = new ArrayList(10);
  49. List schemaImports = new ArrayList(5);
  50. List moduleImports = new ArrayList(5);
  51. private Expression defaultValue = null;
  52. /**
  53. * Constructor for internal use: this class should be instantiated via the QueryModule
  54. */
  55. public QueryParser() {
  56. }
  57. /**
  58. * Create an XQueryExpression
  59. * @param query the source text of the query
  60. * @param staticContext the static context of the query
  61. * @param config the Saxon configuration
  62. * @return the compiled XQuery expression
  63. */
  64. public XQueryExpression makeXQueryExpression(String query,
  65. QueryModule staticContext,
  66. Configuration config) throws XPathException {
  67. try {
  68. if (config.getXMLVersion() == Configuration.XML10) {
  69. query = normalizeLineEndings10(query);
  70. } else {
  71. query = normalizeLineEndings11(query);
  72. }
  73. Executable exec = staticContext.getExecutable();
  74. if (exec == null) {
  75. exec = new Executable(config);
  76. exec.setHostLanguage(Configuration.XQUERY);
  77. staticContext.setExecutable(exec);
  78. }
  79. Properties outputProps = new Properties();
  80. outputProps.setProperty(OutputKeys.METHOD, "xml");
  81. outputProps.setProperty(OutputKeys.INDENT, "yes");
  82. exec.setDefaultOutputProperties(outputProps);
  83. exec.setLocationMap(new LocationMap());
  84. exec.setFunctionLibrary(new ExecutableFunctionLibrary(config));
  85. // this will be changed later
  86. setExecutable(exec);
  87. Expression exp = parseQuery(query, 0, Token.EOF, staticContext);
  88. int loc = env.getLocationMap().allocateLocationId(env.getSystemId(), 1);
  89. exp.setContainer(new TemporaryContainer(staticContext.getLocationMap(), loc));
  90. //staticContext.bindUnboundFunctionCalls();
  91. exec.fixupQueryModules(staticContext);
  92. // Check for cyclic dependencies among the modules
  93. if (!disableCycleChecks) {
  94. Iterator miter = exec.getQueryLibraryModules();
  95. while (miter.hasNext()) {
  96. QueryModule module = (QueryModule)miter.next();
  97. module.lookForModuleCycles(new Stack(), 1);
  98. }
  99. }
  100. // Make the XQueryexpression object
  101. XQueryExpression queryExp = new XQueryExpression(exp, exec, staticContext, config);
  102. //exp = queryExp.getExpression();
  103. // Make the function library that's available at run-time (e.g. for saxon:evaluate()). This includes
  104. // all user-defined functions regardless of which module they are in
  105. FunctionLibrary userlib = exec.getFunctionLibrary();
  106. FunctionLibraryList lib = new FunctionLibraryList();
  107. lib.addFunctionLibrary(
  108. SystemFunctionLibrary.getSystemFunctionLibrary(SystemFunctionLibrary.XPATH_ONLY));
  109. lib.addFunctionLibrary(config.getVendorFunctionLibrary());
  110. lib.addFunctionLibrary(new ConstructorFunctionLibrary(config));
  111. if (config.isAllowExternalFunctions()) {
  112. Configuration.getPlatform().addFunctionLibraries(lib, config, Configuration.XQUERY);
  113. }
  114. lib.addFunctionLibrary(userlib);
  115. exec.setFunctionLibrary(lib);
  116. return queryExp;
  117. } catch (XPathException e) {
  118. if (!e.hasBeenReported()) {
  119. reportError(e);
  120. }
  121. throw e;
  122. }
  123. }
  124. /**
  125. * Normalize line endings in the source query, according to the XML 1.1 rules.
  126. * @param in the input query
  127. * @return the query with line endings normalized
  128. */
  129. private static String normalizeLineEndings11(String in) {
  130. if (in.indexOf((char)0xa) < 0 && in.indexOf((char)0x85) < 0 && in.indexOf((char)0x2028) < 0) {
  131. return in;
  132. }
  133. FastStringBuffer sb = new FastStringBuffer(in.length());
  134. for (int i = 0; i < in.length(); i++) {
  135. char ch = in.charAt(i);
  136. switch (ch) {
  137. case 0x85:
  138. case 0x2028:
  139. sb.append((char)0xa);
  140. break;
  141. case 0xd:
  142. if (i < in.length() - 1 && (in.charAt(i + 1) == (char)0xa || in.charAt(i + 1) == (char)0x85)) {
  143. sb.append((char)0xa);
  144. i++;
  145. } else {
  146. sb.append((char)0xa);
  147. }
  148. break;
  149. default:
  150. sb.append(ch);
  151. }
  152. }
  153. return sb.toString();
  154. }
  155. /**
  156. * Normalize line endings in the source query, according to the XML 1.0 rules.
  157. * @param in the input query
  158. * @return the query text with line endings normalized
  159. */
  160. private static String normalizeLineEndings10(String in) {
  161. if (in.indexOf((char)0xa) < 0) {
  162. return in;
  163. }
  164. FastStringBuffer sb = new FastStringBuffer(in.length());
  165. for (int i = 0; i < in.length(); i++) {
  166. char ch = in.charAt(i);
  167. switch (ch) {
  168. case 0xd:
  169. if (i < in.length() - 1 && in.charAt(i + 1) == (char)0xa) {
  170. sb.append((char)0xa);
  171. i++;
  172. } else {
  173. sb.append((char)0xa);
  174. }
  175. break;
  176. default:
  177. sb.append(ch);
  178. }
  179. }
  180. return sb.toString();
  181. }
  182. /**
  183. * Get the executable containing this expression.
  184. * @return the executable
  185. */
  186. public Executable getExecutable() {
  187. return executable;
  188. }
  189. /**
  190. * Set the executable used for this query expression
  191. * @param exec the executable
  192. */
  193. public void setExecutable(Executable exec) {
  194. executable = exec;
  195. }
  196. /**
  197. * Disable checks for certain kinds of cycle. This is equivalent to
  198. * <p><code>declare option saxon:allow-cycles "true"</code></p>
  199. * @param disable true if checks for import cycles are to be suppressed, that is,
  200. * if cycles should be allowed
  201. */
  202. public void setDisableCycleChecks(boolean disable) {
  203. disableCycleChecks = disable;
  204. }
  205. /**
  206. * Parse a top-level Query.
  207. * Prolog? Expression
  208. *
  209. * @param queryString The text of the query
  210. * @param start Offset of the start of the query
  211. * @param terminator Token expected to follow the query (usually Token.EOF)
  212. * @param env The static context
  213. * @return the Expression object that results from parsing
  214. * @throws net.sf.saxon.trans.XPathException
  215. * if the expression contains a syntax error
  216. */
  217. private Expression parseQuery(String queryString,
  218. int start,
  219. int terminator,
  220. QueryModule env) throws XPathException {
  221. this.env = env;
  222. nameChecker = env.getConfiguration().getNameChecker();
  223. language = XQUERY;
  224. t = new Tokenizer();
  225. try {
  226. t.tokenize(queryString, start, -1, 1);
  227. } catch (XPathException err) {
  228. grumble(err.getMessage());
  229. }
  230. parseVersionDeclaration();
  231. parseProlog();
  232. processPreamble();
  233. Expression exp = parseExpression();
  234. // Diagnostic code - show the expression before any optimizations
  235. // ExpressionPresenter ep = ExpressionPresenter.make(env.getConfiguration());
  236. // exp.explain(ep);
  237. // ep.close();
  238. // End of diagnostic code
  239. if (t.currentToken != terminator) {
  240. grumble("Unexpected token " + currentTokenDisplay() + " beyond end of query");
  241. }
  242. setLocation(exp);
  243. if (errorCount == 0) {
  244. return exp;
  245. } else {
  246. XPathException err = new XPathException("One or more static errors were reported during query analysis");
  247. err.setHasBeenReported();
  248. err.setErrorCode(firstError.getErrorCodeLocalPart()); // largely for the XQTS test driver
  249. throw err;
  250. }
  251. }
  252. /**
  253. * Parse a library module.
  254. * Prolog? Expression
  255. *
  256. * @param queryString The text of the library module.
  257. * @param env The static context. The result of parsing
  258. * a library module is that the static context is populated with a set of function
  259. * declarations and variable declarations. Each library module must have its own
  260. * static context objext.
  261. * @throws XPathException if the expression contains a syntax error
  262. */
  263. public final void parseLibraryModule(String queryString, QueryModule env)
  264. throws XPathException {
  265. this.env = env;
  266. nameChecker = env.getConfiguration().getNameChecker();
  267. executable = env.getExecutable();
  268. t = new Tokenizer();
  269. try {
  270. t.tokenize(queryString, 0, -1, 1);
  271. } catch (XPathException err) {
  272. grumble(err.getMessage());
  273. }
  274. parseVersionDeclaration();
  275. parseModuleDeclaration();
  276. parseProlog();
  277. processPreamble();
  278. if (t.currentToken != Token.EOF) {
  279. grumble("Unrecognized content found after the variable and function declarations in a library module");
  280. }
  281. if (errorCount != 0) {
  282. XPathException err = new XPathException("Static errors were reported in the imported library module");
  283. err.setErrorCode(firstError.getErrorCodeLocalPart());
  284. throw err;
  285. }
  286. }
  287. /**
  288. * Report a static error
  289. *
  290. * @param message the error message
  291. * @throws XPathException always thrown: an exception containing the
  292. * supplied message
  293. */
  294. protected void grumble(String message, String errorCode) throws XPathException {
  295. String s = t.recentText();
  296. ExpressionLocation loc = makeLocator();
  297. String prefix = getLanguage() +
  298. ("XPST0003".equals(errorCode) ? " syntax error " : " static error ") +
  299. (message.startsWith("...") ? "near" : "in") +
  300. " #" + s + "#:\n ";
  301. XPathException exception = new XPathException(prefix + message);
  302. exception.setErrorCode(errorCode);
  303. exception.setLocator(loc);
  304. reportError(exception);
  305. }
  306. private void reportError(XPathException exception) throws XPathException {
  307. errorCount++;
  308. if (firstError == null) {
  309. firstError = exception;
  310. }
  311. ((QueryModule)env).reportFatalError(exception);
  312. throw exception;
  313. }
  314. /**
  315. * Make a Locator object representing the current parsing location
  316. *
  317. * @return a Locator
  318. */
  319. private ExpressionLocation makeLocator() {
  320. int line = t.getLineNumber();
  321. int column = t.getColumnNumber();
  322. ExpressionLocation loc = new ExpressionLocation();
  323. loc.setSystemId(env.getSystemId());
  324. loc.setLineNumber(line);
  325. loc.setColumnNumber(column);
  326. return loc;
  327. }
  328. private static Pattern encNamePattern = Pattern.compile("^[A-Za-z]([A-Za-z0-9._\\x2D])*$");
  329. /**
  330. * Parse the version declaration if present.
  331. *
  332. * @throws XPathException in the event of a syntax error.
  333. */
  334. private void parseVersionDeclaration() throws XPathException {
  335. if (t.currentToken == Token.XQUERY_VERSION) {
  336. nextToken();
  337. expect(Token.STRING_LITERAL);
  338. if (!("1.0".equals(t.currentTokenValue))) {
  339. grumble("XQuery version must be 1.0", "XQST0031");
  340. }
  341. nextToken();
  342. if ("encoding".equals(t.currentTokenValue)) {
  343. nextToken();
  344. expect(Token.STRING_LITERAL);
  345. if (!encNamePattern.matcher(t.currentTokenValue).matches()) {
  346. grumble("Encoding name contains invalid characters", "XQST0087");
  347. }
  348. // we ignore the encoding now: it was handled earlier, while decoding the byte stream
  349. nextToken();
  350. }
  351. expect(Token.SEMICOLON);
  352. nextToken();
  353. }
  354. }
  355. /**
  356. * In a library module, parse the module declaration
  357. * Syntax: <"module" "namespace"> prefix "=" uri ";"
  358. *
  359. * @throws XPathException in the event of a syntax error.
  360. */
  361. private void parseModuleDeclaration() throws XPathException {
  362. expect(Token.MODULE_NAMESPACE);
  363. nextToken();
  364. expect(Token.NAME);
  365. String prefix = t.currentTokenValue;
  366. nextToken();
  367. expect(Token.EQUALS);
  368. nextToken();
  369. expect(Token.STRING_LITERAL);
  370. String uri = URILiteral(t.currentTokenValue);
  371. checkProhibitedPrefixes(prefix, uri);
  372. if (uri.length()==0) {
  373. grumble("Module namespace cannot be \"\"", "XQST0088");
  374. uri = "http://saxon.fallback.namespace/"; // for error recovery
  375. }
  376. nextToken();
  377. expect(Token.SEMICOLON);
  378. nextToken();
  379. try {
  380. ((QueryModule)env).declarePrologNamespace(prefix, uri);
  381. } catch (XPathException err) {
  382. err.setLocator(makeLocator());
  383. reportError(err);
  384. }
  385. ((QueryModule)env).setModuleNamespace(uri);
  386. }
  387. /**
  388. * Parse the query prolog. This method, and its subordinate methods which handle
  389. * individual declarations in the prolog, cause the static context to be updated
  390. * with relevant context information. On exit, t.currentToken is the first token
  391. * that is not recognized as being part of the prolog.
  392. *
  393. * @throws XPathException in the event of a syntax error.
  394. */
  395. private void parseProlog() throws XPathException {
  396. //boolean allowSetters = true;
  397. boolean allowModuleDecl = true;
  398. boolean allowDeclarations = true;
  399. while (true) {
  400. try {
  401. if (t.currentToken == Token.MODULE_NAMESPACE) {
  402. String uri = ((QueryModule)env).getModuleNamespace();
  403. if (uri == null) {
  404. grumble("Module declaration must not be used in a main module");
  405. } else {
  406. grumble("Module declaration appears more than once");
  407. }
  408. if (!allowModuleDecl) {
  409. grumble("Module declaration must precede other declarations in the query prolog");
  410. }
  411. }
  412. allowModuleDecl = false;
  413. switch (t.currentToken) {
  414. case Token.DECLARE_NAMESPACE:
  415. if (!allowDeclarations) {
  416. grumble("Namespace declarations cannot follow variables, functions, or options");
  417. }
  418. //allowSetters = false;
  419. parseNamespaceDeclaration();
  420. break;
  421. case Token.DECLARE_DEFAULT:
  422. nextToken();
  423. expect(Token.NAME);
  424. if (t.currentTokenValue.equals("element")) {
  425. if (!allowDeclarations) {
  426. grumble("Namespace declarations cannot follow variables, functions, or options");
  427. }
  428. //allowSetters = false;
  429. parseDefaultElementNamespace();
  430. } else if (t.currentTokenValue.equals("function")) {
  431. if (!allowDeclarations) {
  432. grumble("Namespace declarations cannot follow variables, functions, or options");
  433. }
  434. //allowSetters = false;
  435. parseDefaultFunctionNamespace();
  436. } else if (t.currentTokenValue.equals("collation")) {
  437. if (!allowDeclarations) {
  438. grumble("Collation declarations must appear earlier in the prolog");
  439. }
  440. parseDefaultCollation();
  441. } else if (t.currentTokenValue.equals("order")) {
  442. if (!allowDeclarations) {
  443. grumble("Order declarations must appear earlier in the prolog");
  444. }
  445. parseDefaultOrder();
  446. } else {
  447. grumble("After 'declare default', expected 'element', 'function', or 'collation'");
  448. }
  449. break;
  450. case Token.DECLARE_BOUNDARY_SPACE:
  451. if (!allowDeclarations) {
  452. grumble("'declare boundary-space' must appear earlier in the query prolog");
  453. }
  454. parseBoundarySpaceDeclaration();
  455. break;
  456. case Token.DECLARE_ORDERING:
  457. if (!allowDeclarations) {
  458. grumble("'declare ordering' must appear earlier in the query prolog");
  459. }
  460. parseOrderingDeclaration();
  461. break;
  462. case Token.DECLARE_COPY_NAMESPACES:
  463. if (!allowDeclarations) {
  464. grumble("'declare copy-namespaces' must appear earlier in the query prolog");
  465. }
  466. parseCopyNamespacesDeclaration();
  467. break;
  468. case Token.DECLARE_BASEURI:
  469. if (!allowDeclarations) {
  470. grumble("'declare base-uri' must appear earlier in the query prolog");
  471. }
  472. parseBaseURIDeclaration();
  473. break;
  474. case Token.IMPORT_SCHEMA:
  475. //allowSetters = false;
  476. if (!allowDeclarations) {
  477. grumble("Import schema must appear earlier in the prolog");
  478. }
  479. parseSchemaImport();
  480. break;
  481. case Token.IMPORT_MODULE:
  482. //allowSetters = false;
  483. if (!allowDeclarations) {
  484. grumble("Import module must appear earlier in the prolog");
  485. }
  486. parseModuleImport();
  487. break;
  488. case Token.DECLARE_VARIABLE:
  489. //allowSetters = false;
  490. if (allowDeclarations) {
  491. sealNamespaces(namespacesToBeSealed, env.getConfiguration());
  492. allowDeclarations = false;
  493. }
  494. processPreamble();
  495. parseVariableDeclaration();
  496. break;
  497. case Token.DECLARE_FUNCTION:
  498. if (allowDeclarations) {
  499. sealNamespaces(namespacesToBeSealed, env.getConfiguration());
  500. allowDeclarations = false;
  501. }
  502. processPreamble();
  503. parseFunctionDeclaration(false);
  504. break;
  505. case Token.DECLARE_UPDATING:
  506. nextToken();
  507. if (!isKeyword("function")) {
  508. grumble("expected 'function' after 'declare updating");
  509. }
  510. if (allowDeclarations) {
  511. sealNamespaces(namespacesToBeSealed, env.getConfiguration());
  512. allowDeclarations = false;
  513. }
  514. processPreamble();
  515. parseUpdatingFunctionDeclaration();
  516. break;
  517. case Token.DECLARE_OPTION:
  518. if (allowDeclarations) {
  519. sealNamespaces(namespacesToBeSealed, env.getConfiguration());
  520. allowDeclarations = false;
  521. }
  522. parseOptionDeclaration();
  523. break;
  524. case Token.DECLARE_CONSTRUCTION:
  525. if (!allowDeclarations) {
  526. grumble("'declare construction' must appear earlier in the query prolog");
  527. }
  528. parseConstructionDeclaration();
  529. break;
  530. case Token.DECLARE_REVALIDATION:
  531. if (!allowDeclarations) {
  532. grumble("'declare revalidation' must appear earlier in the query prolog");
  533. }
  534. parseRevalidationDeclaration();
  535. break;
  536. default:
  537. return;
  538. }
  539. expect(Token.SEMICOLON);
  540. nextToken();
  541. } catch (XPathException err) {
  542. if (err.getLocator() == null) {
  543. err.setLocator(makeLocator());
  544. }
  545. if (!err.hasBeenReported()) {
  546. errorCount++;
  547. if (firstError == null) {
  548. firstError = err;
  549. }
  550. ((QueryModule)env).reportFatalError(err);
  551. }
  552. // we've reported an error, attempt to recover by skipping to the
  553. // next semicolon
  554. while (t.currentToken != Token.SEMICOLON) {
  555. nextToken();
  556. if (t.currentToken == Token.EOF) {
  557. return;
  558. } else if (t.currentToken == Token.RCURLY) {
  559. t.lookAhead();
  560. } else if (t.currentToken == Token.TAG) {
  561. parsePseudoXML(true);
  562. }
  563. }
  564. nextToken();
  565. }
  566. }
  567. }
  568. private void sealNamespaces(List namespacesToBeSealed, Configuration config) {
  569. for (Iterator iter = namespacesToBeSealed.iterator(); iter.hasNext();) {
  570. String ns = (String)iter.next();
  571. config.sealNamespace(ns);
  572. }
  573. }
  574. /**
  575. * Method called once the setters have been read to do tidying up that can't be done until we've got
  576. * to the end
  577. *
  578. * @throws XPathException
  579. */
  580. private void processPreamble() throws XPathException {
  581. if (preambleProcessed) {
  582. return;
  583. }
  584. preambleProcessed = true;
  585. if (foundDefaultCollation) {
  586. String collationName = env.getDefaultCollationName();
  587. URI collationURI;
  588. try {
  589. collationURI = new URI(collationName);
  590. if (!collationURI.isAbsolute()) {
  591. URI base = new URI(env.getBaseURI());
  592. collationURI = base.resolve(collationURI);
  593. collationName = collationURI.toString();
  594. }
  595. } catch (URISyntaxException err) {
  596. grumble("Default collation name '" + collationName + "' is not a valid URI");
  597. collationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
  598. }
  599. if (env.getCollation(collationName) == null) {
  600. grumble("Default collation name '" + collationName + "' is not a recognized collation", "XQST0038");
  601. collationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
  602. }
  603. ((QueryModule)env).setDefaultCollationName(collationName);
  604. }
  605. for (Iterator iter = schemaImports.iterator(); iter.hasNext();) {
  606. Import imp = (Import)iter.next();
  607. applySchemaImport(imp);
  608. }
  609. for (Iterator iter = moduleImports.iterator(); iter.hasNext();) {
  610. Import imp = (Import)iter.next();
  611. // Check that this import would not create a cycle involving a change of namespace
  612. if (!disableCycleChecks) {
  613. if (!imp.namespaceURI.equals(((QueryModule)env).getModuleNamespace())) {
  614. QueryModule parent = (QueryModule)env;
  615. if (!parent.mayImportModule(imp.namespaceURI)) {
  616. XPathException err = new XPathException(
  617. "A module cannot import itself directly or indirectly, unless all modules in the cycle are in the same namespace");
  618. err.setErrorCode("XQST0073");
  619. err.setIsStaticError(true);
  620. throw err;
  621. }
  622. }
  623. }
  624. applyModuleImport(imp);
  625. }
  626. }
  627. private void parseDefaultCollation() throws XPathException {
  628. // <"default" "collation"> StringLiteral
  629. if (foundDefaultCollation) {
  630. grumble("default collation appears more than once", "XQST0038");
  631. }
  632. foundDefaultCollation = true;
  633. nextToken();
  634. expect(Token.STRING_LITERAL);
  635. String uri = URILiteral(t.currentTokenValue);
  636. ((QueryModule)env).setDefaultCollationName(uri);
  637. nextToken();
  638. }
  639. /**
  640. * parse "declare default order empty (least|greatest)"
  641. */
  642. private void parseDefaultOrder() throws XPathException {
  643. if (foundEmptyOrderingDeclaration) {
  644. grumble("empty ordering declaration appears more than once", "XQST0069");
  645. }
  646. foundEmptyOrderingDeclaration = true;
  647. nextToken();
  648. if (!isKeyword("empty")) {
  649. grumble("After 'declare default order', expected keyword 'empty'");
  650. }
  651. nextToken();
  652. if (isKeyword("least")) {
  653. ((QueryModule)env).setEmptyLeast(true);
  654. } else if (isKeyword("greatest")) {
  655. ((QueryModule)env).setEmptyLeast(false);
  656. } else {
  657. grumble("After 'declare default order empty', expected keyword 'least' or 'greatest'");
  658. }
  659. nextToken();
  660. }
  661. /**
  662. * Parse the "declare xmlspace" declaration.
  663. * Syntax: <"declare" "boundary-space"> ("preserve" | "strip")
  664. *
  665. * @throws XPathException
  666. */
  667. private void parseBoundarySpaceDeclaration() throws XPathException {
  668. if (foundBoundarySpaceDeclaration) {
  669. grumble("'declare boundary-space' appears more than once", "XQST0068");
  670. }
  671. foundBoundarySpaceDeclaration = true;
  672. nextToken();
  673. expect(Token.NAME);
  674. if ("preserve".equals(t.currentTokenValue)) {
  675. ((QueryModule)env).setPreserveBoundarySpace(true);
  676. } else if ("strip".equals(t.currentTokenValue)) {
  677. ((QueryModule)env).setPreserveBoundarySpace(false);
  678. } else {
  679. grumble("boundary-space must be 'preserve' or 'strip'");
  680. }
  681. nextToken();
  682. }
  683. /**
  684. * Parse the "declare ordering" declaration.
  685. * Syntax: <"declare" "ordering"> ("ordered" | "unordered")
  686. *
  687. * @throws XPathException
  688. */
  689. private void parseOrderingDeclaration() throws XPathException {
  690. if (foundOrderingDeclaration) {
  691. grumble("ordering mode declaration appears more than once", "XQST0065");
  692. }
  693. foundOrderingDeclaration = true;
  694. nextToken();
  695. expect(Token.NAME);
  696. if ("ordered".equals(t.currentTokenValue)) {
  697. // no action
  698. } else if ("unordered".equals(t.currentTokenValue)) {
  699. // no action
  700. } else {
  701. grumble("ordering mode must be 'ordered' or 'unordered'");
  702. }
  703. nextToken();
  704. }
  705. /**
  706. * Parse the "declare copy-namespaces" declaration.
  707. * Syntax: <"declare" "copy-namespaces"> ("preserve" | "no-preserve") "," ("inherit" | "no-inherit")
  708. *
  709. * @throws XPathException
  710. */
  711. private void parseCopyNamespacesDeclaration() throws XPathException {
  712. if (foundCopyNamespaces) {
  713. grumble("declare inherit-namespaces appears more than once", "XQST0055");
  714. }
  715. foundCopyNamespaces = true;
  716. nextToken();
  717. expect(Token.NAME);
  718. if ("preserve".equals(t.currentTokenValue)) {
  719. ((QueryModule)env).setPreserveNamespaces(true);
  720. } else if ("no-preserve".equals(t.currentTokenValue)) {
  721. ((QueryModule)env).setPreserveNamespaces(false);
  722. } else {
  723. grumble("copy-namespaces must be followed by 'preserve' or 'no-preserve'");
  724. }
  725. nextToken();
  726. expect(Token.COMMA);
  727. nextToken();
  728. expect(Token.NAME);
  729. if ("inherit".equals(t.currentTokenValue)) {
  730. ((QueryModule)env).setInheritNamespaces(true);
  731. } else if ("no-inherit".equals(t.currentTokenValue)) {
  732. ((QueryModule)env).setInheritNamespaces(false);
  733. } else {
  734. grumble("After the comma in the copy-namespaces declaration, expected 'inherit' or 'no-inherit'");
  735. }
  736. nextToken();
  737. }
  738. /**
  739. * Parse the "declare construction" declaration.
  740. * Syntax: <"declare" "construction"> ("preserve" | "strip")
  741. *
  742. * @throws XPathException
  743. */
  744. private void parseConstructionDeclaration() throws XPathException {
  745. if (foundConstructionDeclaration) {
  746. grumble("declare construction appears more than once", "XQST0067");
  747. }
  748. foundConstructionDeclaration = true;
  749. nextToken();
  750. expect(Token.NAME);
  751. int val;
  752. if ("preserve".equals(t.currentTokenValue)) {
  753. val = Validation.PRESERVE;
  754. } else if ("strip".equals(t.currentTokenValue)) {
  755. val = Validation.STRIP;
  756. } else {
  757. grumble("construction mode must be 'preserve' or 'strip'");
  758. val = Validation.STRIP;
  759. }
  760. ((QueryModule)env).setConstructionMode(val);
  761. nextToken();
  762. }
  763. /**
  764. * Parse the "declare revalidation" declaration.
  765. * Syntax: not allowed unless XQuery update is in use
  766. *
  767. * @throws XPathException
  768. */
  769. protected void parseRevalidationDeclaration() throws XPathException {
  770. grumble("declare revalidation is allowed only in XQuery Update");
  771. }
  772. /**
  773. * Parse (and process) the schema import declaration.
  774. * SchemaImport ::= "import" "schema" SchemaPrefix? URILiteral ("at" URILiteral ("," URILiteral)*)?
  775. * SchemaPrefix ::= ("namespace" NCName "=") | ("default" "element" "namespace")
  776. */
  777. private void parseSchemaImport() throws XPathException {
  778. if (!env.getConfiguration().isSchemaAware(Configuration.XQUERY)) {
  779. grumble("To import a schema, you need the schema-aware version of Saxon", "XQST0009");
  780. }
  781. Import sImport = new Import();
  782. String prefix = null;
  783. sImport.namespaceURI = null;
  784. sImport.locationURIs = new ArrayList(5);
  785. nextToken();
  786. if (isKeyword("namespace")) {
  787. t.setState(Tokenizer.DEFAULT_STATE);
  788. nextToken();
  789. expect(Token.NAME);
  790. prefix = t.currentTokenValue;
  791. nextToken();
  792. expect(Token.EQUALS);
  793. nextToken();
  794. } else if (isKeyword("default")) {
  795. nextToken();
  796. if (!isKeyword("element")) {
  797. grumble("In 'import schema', expected 'element namespace'");
  798. }
  799. nextToken();
  800. if (!isKeyword("namespace")) {
  801. grumble("In 'import schema', expected keyword 'namespace'");
  802. }
  803. nextToken();
  804. prefix = "";
  805. }
  806. if (t.currentToken == Token.STRING_LITERAL) {
  807. String uri = URILiteral(t.currentTokenValue);
  808. checkProhibitedPrefixes(prefix, uri);
  809. sImport.namespaceURI = uri;
  810. nextToken();
  811. if (isKeyword("at")) {
  812. nextToken();
  813. expect(Token.STRING_LITERAL);
  814. sImport.locationURIs.add(URILiteral(t.currentTokenValue));
  815. nextToken();
  816. while (t.currentToken == Token.COMMA) {
  817. nextToken();
  818. expect(Token.STRING_LITERAL);
  819. sImport.locationURIs.add(URILiteral(t.currentTokenValue));
  820. nextToken();
  821. }
  822. } else if (t.currentToken != Token.SEMICOLON) {
  823. grumble("After the target namespace URI, expected 'at' or ';'");
  824. }
  825. } else {
  826. grumble("After 'import schema', expected 'namespace', 'default', or a string-literal");
  827. }
  828. if (prefix != null) {
  829. try {
  830. if (prefix.length() == 0) {
  831. ((QueryModule)env).setDefaultElementNamespace(sImport.namespaceURI);
  832. } else {
  833. if (sImport.namespaceURI == null || "".equals(sImport.namespaceURI)) {
  834. grumble("A prefix cannot be bound to the null namespace", "XQST0057");
  835. }
  836. ((QueryModule)env).declarePrologNamespace(prefix, sImport.namespaceURI);
  837. }
  838. } catch (XPathException err) {
  839. err.setLocator(makeLocator());
  840. reportError(err);
  841. }
  842. }
  843. for (Iterator iter = schemaImports.iterator(); iter.hasNext();) {
  844. Import imp = (Import)iter.next();
  845. if (imp.namespaceURI.equals(sImport.namespaceURI)) {
  846. grumble("Schema namespace '" + sImport.namespaceURI + "' is imported more than once", "XQST0058");
  847. break;
  848. }
  849. }
  850. schemaImports.add(sImport);
  851. }
  852. private void applySchemaImport(Import sImport) throws XPathException {
  853. // Do the importing
  854. Configuration config = env.getConfiguration();
  855. if (!config.isSchemaAvailable(sImport.namespaceURI)) {
  856. if (!sImport.locationURIs.isEmpty()) {
  857. try {
  858. PipelineConfiguration pipe = config.makePipelineConfiguration();
  859. config.readMultipleSchemas(pipe, env.getBaseURI(), sImport.locationURIs, sImport.namespaceURI);
  860. namespacesToBeSealed.add(sImport.namespaceURI);
  861. } catch (TransformerConfigurationException err) {
  862. grumble("Error in schema. " + err.getMessage(), "XQST0059");
  863. }
  864. } else {
  865. grumble("Unable to locate requested schema", "XQST0059");
  866. }
  867. }
  868. ((QueryModule)env).addImportedSchema(sImport.namespaceURI, env.getBaseURI(), sImport.locationURIs);
  869. }
  870. /**
  871. * Parse (and expand) the module import declaration.
  872. * Syntax: <"import" "module" ("namespace" NCName "=")? uri ("at" uri ("," uri)*)? ";"
  873. */
  874. private void parseModuleImport() throws XPathException {
  875. QueryModule thisModule = (QueryModule)env;
  876. Import mImport = new Import();
  877. String prefix = null;
  878. mImport.namespaceURI = null;
  879. mImport.locationURIs = new ArrayList(5);
  880. nextToken();
  881. if (t.currentToken == Token.NAME && t.currentTokenValue.equals("namespace")) {
  882. t.setState(Tokenizer.DEFAULT_STATE);
  883. nextToken();
  884. expect(Token.NAME);
  885. prefix = t.currentTokenValue;
  886. nextToken();
  887. expect(Token.EQUALS);
  888. nextToken();
  889. }
  890. if (t.currentToken == Token.STRING_LITERAL) {
  891. String uri = URILiteral(t.currentTokenValue);
  892. checkProhibitedPrefixes(prefix, uri);
  893. mImport.namespaceURI = uri;
  894. if (mImport.namespaceURI.length() == 0) {
  895. grumble("Imported module namespace cannot be \"\"", "XQST0088");
  896. mImport.namespaceURI = "http://saxon.fallback.namespace/line" + t.getLineNumber(); // for error recovery
  897. }
  898. if (importedModules.contains(mImport.namespaceURI)) {
  899. grumble("Two 'import module' declarations specify the same module namespace", "XQST0047");
  900. }
  901. importedModules.add(mImport.namespaceURI);
  902. ((QueryModule)env).addImportedNamespace(mImport.namespaceURI);
  903. nextToken();
  904. if (isKeyword("at")) {
  905. do {
  906. nextToken();
  907. expect(Token.STRING_LITERAL);
  908. mImport.locationURIs.add(URILiteral(t.currentTokenValue));
  909. nextToken();
  910. } while (t.currentToken == Token.COMMA);
  911. }
  912. } else {
  913. grumble("After 'import module', expected 'namespace' or a string-literal");
  914. }
  915. if (prefix != null) {
  916. try {
  917. thisModule.declarePrologNamespace(prefix, mImport.namespaceURI);
  918. } catch (XPathException err) {
  919. err.setLocator(makeLocator());
  920. reportError(err);
  921. }
  922. }
  923. // // Check that this import would not create a cycle involving a change of namespace
  924. // if (!disableCycleChecks) {
  925. // if (!mImport.namespaceURI.equals(((QueryModule)env).getModuleNamespace())) {
  926. // QueryModule parent = (QueryModule)env;
  927. // if (!parent.mayImport(mImport.namespaceURI)) {
  928. // StaticError err = new StaticError("A module cannot import itself directly or indirectly, unless all modules in the cycle are in the same namespace");
  929. // err.setErrorCode("XQST0073");
  930. // throw err;
  931. // }
  932. // }
  933. // }
  934. moduleImports.add(mImport);
  935. }
  936. public void applyModuleImport(Import mImport) throws XPathException {
  937. boolean foundOne = false;
  938. // resolve the location URIs against the base URI
  939. Platform platform = Configuration.getPlatform();
  940. for (int i=0; i<mImport.locationURIs.size(); i++) {
  941. try {
  942. String uri = (String)mImport.locationURIs.get(i);
  943. URI abs = platform.makeAbsolute(uri, env.getBaseURI());
  944. mImport.locationURIs.set(i, abs);
  945. } catch (URISyntaxException e) {
  946. grumble("Invalid URI " + mImport.locationURIs.get(i) + ": " + e.getMessage());
  947. }
  948. }
  949. // If any of the modules are already loaded, don't re-read them; but do check that none of them
  950. // references the current module namespace directly or indirectly
  951. List existingModules = executable.getQueryLibraryModules(mImport.namespaceURI);
  952. if (existingModules != null) {
  953. //System.err.println("Number of existing modules: " + existingModules.size());
  954. for (int m = 0; m < existingModules.size(); m++) {
  955. QueryModule importedModule = (QueryModule)existingModules.get(m);
  956. //System.err.println("Existing module location URI = " + importedModule.getLocationURI());
  957. if (!importedModule.getLocationURI().equals(((QueryModule)env).getLocationURI())) {
  958. //QueryReader.importModuleContents(importedModule, (QueryModule)env);
  959. foundOne = true;
  960. }
  961. if (!disableCycleChecks &&
  962. ((QueryModule)env).getModuleNamespace() != null &&
  963. !((QueryModule)env).getModuleNamespace().equals(importedModule.getModuleNamespace()) &&
  964. importedModule.importsNamespaceIndirectly(((QueryModule)env).getModuleNamespace())) {
  965. grumble("A cycle exists among the module imports, involving namespaces " +
  966. ((QueryModule)env).getModuleNamespace() + " and " +
  967. importedModule.getModuleNamespace());
  968. }
  969. for (int h = mImport.locationURIs.size() - 1; h >= 0; h--) {
  970. if (mImport.locationURIs.get(h).equals(importedModule.getLocationURI())) {
  971. mImport.locationURIs.remove(h);
  972. }
  973. }
  974. }
  975. }
  976. // If we've found at least one module, and there are no location URIs left, call it a day.
  977. if (mImport.locationURIs.isEmpty() && foundOne) {
  978. return;
  979. }
  980. // Call the module URI resolver to find the remaining modules
  981. ModuleURIResolver resolver = ((QueryModule)env).getUserQueryContext().getModuleURIResolver();
  982. String[] hints = new String[mImport.locationURIs.size()];
  983. for (int h=0; h<hints.length; h++) {
  984. hints[h] = mImport.locationURIs.get(h).toString();
  985. }
  986. StreamSource[] sources = null;
  987. if (resolver != null) {
  988. sources = resolver.resolve(mImport.namespaceURI, env.getBaseURI(), hints);
  989. }
  990. if (sources == null) {
  991. if (hints.length == 0) {
  992. if (existingModules == null) {
  993. grumble("Cannot locate module for namespace " + mImport.namespaceURI, "XQST0059");
  994. }
  995. }
  996. resolver = env.getConfiguration().getStandardModuleURIResolver();
  997. sources = resolver.resolve(mImport.namespaceURI, env.getBaseURI(), hints);
  998. }
  999. for (int m = 0; m < sources.length; m++) {
  1000. StreamSource ss = sources[m];
  1001. String baseURI = ss.getSystemId();
  1002. if (baseURI == null) {
  1003. if (m < hints.length) {
  1004. baseURI = hints[m];
  1005. ss.setSystemId(hints[m]);
  1006. } else {
  1007. grumble("No base URI available for imported module", "XQST0059");
  1008. }
  1009. }
  1010. // Although the module hadn't been loaded when we started, it might have been loaded since, as
  1011. // a result of a reference from another imported module. Note, we are careful here to use URI.equals()
  1012. // rather that String.equals() to compare URIs, as this gives a slightly more intelligent comparison,
  1013. // for example the scheme name is case-independent, and file:///x/y/z matches file:/x/y/z.
  1014. // TODO: use similar logic when loading schema modules
  1015. existingModules = executable.getQueryLibraryModules(mImport.namespaceURI);
  1016. boolean loaded = false;
  1017. if (existingModules != null && m < hints.length) {
  1018. for (int e=0; e<existingModules.size(); e++) {
  1019. if (((QueryModule)existingModules.get(e)).getLocationURI().equals(mImport.locationURIs.get(m))) {
  1020. loaded = true;
  1021. break;
  1022. }
  1023. }
  1024. }
  1025. if (loaded) {
  1026. break;
  1027. }
  1028. try {
  1029. String queryText = QueryReader.readSourceQuery(ss, nameChecker);
  1030. try {
  1031. if (ss.getInputStream() != null) {
  1032. ss.getInputStream().close();
  1033. } else if (ss.getReader() != null) {
  1034. ss.getReader().close();
  1035. }
  1036. } catch (IOException e) {
  1037. throw new XPathException("Failure while closing file for imported query module");
  1038. }
  1039. QueryModule.makeQueryModule(
  1040. baseURI, executable, (QueryModule)env, queryText, mImport.namespaceURI,
  1041. disableCycleChecks);
  1042. } catch (XPathException err) {
  1043. err.maybeSetLocation(makeLocator());
  1044. reportError(err);
  1045. }
  1046. }
  1047. }
  1048. /**
  1049. * Parse the Base URI declaration.
  1050. * Syntax: <"declare" "base-uri"> uri-literal
  1051. *
  1052. * @throws XPathException
  1053. */
  1054. private void parseBaseURIDeclaration() throws XPathException {
  1055. if (foundBaseURIDeclaration) {
  1056. grumble("Base URI Declaration may only appear once", "XQST0032");
  1057. }
  1058. foundBaseURIDeclaration = true;
  1059. nextToken();
  1060. expect(Token.STRING_LITERAL);
  1061. String uri = URILiteral(t.currentTokenValue);
  1062. try {
  1063. // if the supplied URI is relative, try to resolve it
  1064. URI baseURI = new URI(uri);
  1065. if (!baseURI.isAbsolute()) {
  1066. String oldBase = env.getBaseURI();
  1067. URI oldBaseURI = new URI(oldBase);
  1068. uri = oldBaseURI.resolve(uri).toString();
  1069. }
  1070. ((QueryModule)env).setBaseURI(uri);
  1071. } catch (URISyntaxException err) {
  1072. // The spec says this "is not intrinsically an error", but can cause a failure later
  1073. ((QueryModule)env).setBaseURI(uri);
  1074. }
  1075. nextToken();
  1076. }
  1077. /**
  1078. * Parse the "default function namespace" declaration.
  1079. * Syntax: <"declare" "default" "function" "namespace"> StringLiteral
  1080. *
  1081. * @throws XPathException to indicate a syntax error
  1082. */
  1083. private void parseDefaultFunctionNamespace() throws XPathException {
  1084. if (foundDefaultFunctionNamespace) {
  1085. grumble("default function namespace appears more than once", "XQST0066");
  1086. }
  1087. foundDefaultFunctionNamespace = true;
  1088. nextToken();
  1089. expect(Token.NAME);
  1090. if (!"namespace".equals(t.currentTokenValue)) {
  1091. grumble("After 'declare default function', expected 'namespace'");
  1092. }
  1093. nextToken();
  1094. expect(Token.STRING_LITERAL);
  1095. String uri = URILiteral(t.currentTokenValue);
  1096. ((QueryModule)env).setDefaultFunctionNamespace(uri);
  1097. nextToken();
  1098. }
  1099. /**
  1100. * Parse the "default element namespace" declaration.
  1101. * Syntax: <"declare" "default" "element" "namespace"> StringLiteral
  1102. *
  1103. * @throws XPathException to indicate a syntax error
  1104. */
  1105. private void parseDefaultElementNamespace() throws XPathException {
  1106. if (foundDefaultElementNamespace) {
  1107. grumble("default element namespace appears more than once", "XQST0066");
  1108. }
  1109. foundDefaultElementNamespace = true;
  1110. nextToken();
  1111. expect(Token.NAME);
  1112. if (!"namespace".equals(t.currentTokenValue)) {
  1113. grumble("After 'declare default element', expected 'namespace'");
  1114. }
  1115. nextToken();
  1116. expec

Large files files are truncated, but you can click here to view the full file