0001: /*
0002: * Copyright 2004 Outerthought bvba and Schaubroeck nv
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package org.outerj.daisy.repository.test;
0017:
0018: import org.outerj.daisy.repository.testsupport.AbstractDaisyTestCase;
0019: import org.outerj.daisy.repository.testsupport.DOMBuilder;
0020: import org.outerj.daisy.repository.*;
0021: import org.outerj.daisy.repository.acl.*;
0022: import org.outerj.daisy.repository.user.UserManager;
0023: import org.outerj.daisy.repository.user.Role;
0024: import org.outerj.daisy.repository.user.User;
0025: import org.outerj.daisy.repository.schema.RepositorySchema;
0026: import org.outerj.daisy.repository.schema.PartType;
0027: import org.outerj.daisy.repository.schema.DocumentType;
0028: import org.outerj.daisy.repository.schema.FieldType;
0029: import org.outerj.daisy.navigation.*;
0030: import org.jaxen.dom.DOMXPath;
0031: import org.apache.xmlbeans.XmlObject;
0032: import org.w3c.dom.Element;
0033:
0034: import java.util.List;
0035: import java.util.Locale;
0036:
0037: /**
0038: * Tests the NavigationManager. Of this test there is no
0039: * distinction between Remote and Local, since there is no
0040: * remote API implementation of this component (the remote
0041: * usage is directly using the HTTP interface).
0042: */
0043: public abstract class AbstractNavigationTest extends
0044: AbstractDaisyTestCase {
0045: protected boolean resetDataStores() {
0046: return true;
0047: }
0048:
0049: protected abstract RepositoryManager getRepositoryManager()
0050: throws Exception;
0051:
0052: public void testNavigation() throws Exception {
0053: RepositoryManager repositoryManager = getRepositoryManager();
0054: Repository repository = repositoryManager
0055: .getRepository(new Credentials("testuser", "testuser"));
0056: repository.switchRole(Role.ADMINISTRATOR);
0057: NavigationManager navigationManager = (NavigationManager) repository
0058: .getExtension("NavigationManager");
0059:
0060: RepositorySchema schema = repository.getRepositorySchema();
0061:
0062: // Create types required by navigation stuff
0063: PartType navigationDescriptionType = schema.createPartType(
0064: "NavigationDescription", "text/xml");
0065: navigationDescriptionType.save();
0066:
0067: DocumentType navigationType = schema
0068: .createDocumentType("Navigation");
0069: navigationType.addPartType(navigationDescriptionType, true);
0070: navigationType.save();
0071:
0072: // Create types for other documents
0073: FieldType stringField = schema.createFieldType("StringField",
0074: ValueType.STRING);
0075: stringField.save();
0076:
0077: DocumentType documentType = schema
0078: .createDocumentType("doctype");
0079: documentType.addFieldType(stringField, false);
0080: documentType.save();
0081:
0082: // Create a user
0083: UserManager userManager = repository.getUserManager();
0084: Role userRole = userManager.getRole("User", false);
0085:
0086: User user1 = userManager.createUser("user1");
0087: user1.setPassword("user1");
0088: user1.addToRole(userRole);
0089: user1.setDefaultRole(userRole);
0090: user1.save();
0091: Repository user1Repository = repositoryManager
0092: .getRepository(new Credentials("user1", "user1"));
0093: NavigationManager user1NavigationManager = (NavigationManager) user1Repository
0094: .getExtension("NavigationManager");
0095:
0096: // Create an ACL
0097: AccessManager accessManager = repository.getAccessManager();
0098: Acl acl = accessManager.getStagingAcl();
0099:
0100: AclObject aclObject = acl.createNewObject("true");
0101: AclEntry aclEntry = aclObject.createNewEntry(
0102: AclSubjectType.EVERYONE, -1);
0103: aclEntry.set(AclPermission.READ, AclActionType.GRANT);
0104: aclEntry.set(AclPermission.WRITE, AclActionType.GRANT);
0105: aclEntry.set(AclPermission.PUBLISH, AclActionType.GRANT);
0106: aclEntry.set(AclPermission.DELETE, AclActionType.GRANT);
0107: aclObject.add(aclEntry);
0108: acl.add(aclObject);
0109:
0110: aclObject = acl.createNewObject("documentType = 'Navigation'");
0111: aclEntry = aclObject
0112: .createNewEntry(AclSubjectType.EVERYONE, -1);
0113: AccessDetails accessDetails = aclEntry.createNewDetails();
0114: accessDetails.set(AclDetailPermission.NON_LIVE,
0115: AclActionType.DENY);
0116: aclEntry.set(AclPermission.READ, AclActionType.GRANT,
0117: accessDetails);
0118: aclEntry.set(AclPermission.WRITE, AclActionType.DENY);
0119: aclEntry.set(AclPermission.PUBLISH, AclActionType.DENY);
0120: aclEntry.set(AclPermission.DELETE, AclActionType.DENY);
0121: aclObject.add(aclEntry);
0122: acl.add(aclObject);
0123:
0124: acl.save();
0125: accessManager.copyStagingToLive();
0126:
0127: // Create a collection
0128: CollectionManager collectionManager = repository
0129: .getCollectionManager();
0130: DocumentCollection collection1 = collectionManager
0131: .createCollection("collection1");
0132: collection1.save();
0133:
0134: // Create some documents
0135: Document document1 = repository.createDocument("Document 1",
0136: documentType.getId());
0137: document1.setField(stringField.getId(), "X");
0138: document1.setPrivate(true);
0139: document1.addToCollection(collection1);
0140: document1.save();
0141:
0142: Document document2 = user1Repository.createDocument(
0143: "Document 2", documentType.getId());
0144: document2.setField(stringField.getId(), "X");
0145: document2.addToCollection(collection1);
0146: document2.save();
0147:
0148: Document document3 = user1Repository.createDocument(
0149: "Document 3", documentType.getId());
0150: document3.setField(stringField.getId(), "Y");
0151: document3.save();
0152:
0153: DOMBuilder domBuilder;
0154: DOMXPath xpath;
0155: org.w3c.dom.Document navResult;
0156:
0157: // Create navigation documents
0158: Document navdoc2 = repository.createDocument("navdoc2",
0159: navigationType.getId());
0160: StringBuilder content2 = new StringBuilder();
0161: content2
0162: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0163: content2.append(" <d:doc id='" + document3.getId() + "'/>");
0164: content2
0165: .append(" <d:query q=\"select $StringField where documentType != 'Navigation' order by $StringField\"/>");
0166: content2.append("</d:navigationTree>");
0167: navdoc2.setPart("NavigationDescription", "text/xml", content2
0168: .toString().getBytes("UTF-8"));
0169: navdoc2.save();
0170:
0171: Document navdoc1 = repository.createDocument("navdoc1",
0172: navigationType.getId());
0173: StringBuilder content1 = new StringBuilder();
0174: content1
0175: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0176: content1.append(" <d:collections>");
0177: content1.append(" <d:collection name='collection1'/>");
0178: content1.append(" </d:collections>");
0179: content1.append(" <d:doc id='" + document3.getId() + "'>");
0180: content1.append(" <d:doc id='" + document3.getId() + "'/>");
0181: content1.append(" <d:doc id='" + document1.getId() + "'/>");
0182: content1.append(" </d:doc>");
0183: content1.append(" <d:group label='My Group' id='mygroup'>");
0184: content1.append(" <d:group label='My Second Group'>");
0185: content1.append(" <d:doc id='").append(document3.getId())
0186: .append("'/>");
0187: content1
0188: .append(" <d:group label='empty group' id='emptygroup'/>");
0189: content1.append(" </d:group>");
0190: content1.append(" <d:group label='My Third Group'>");
0191: content1.append(" <d:import docId='" + navdoc2.getId()
0192: + "'/>");
0193: content1.append(" </d:group>");
0194: content1.append(" <d:query q='select name where true'/>");
0195: content1.append(" </d:group>");
0196: content1
0197: .append(" <d:link url='http://www.google.com' label='Google'>");
0198: content1
0199: .append(" <d:link url='http://www.apache.org' label='Apache'/>");
0200: content1.append(" </d:link>");
0201: content1.append("</d:navigationTree>");
0202: navdoc1.setPart("NavigationDescription", "text/xml", content1
0203: .toString().getBytes("UTF-8"));
0204: navdoc1.save();
0205:
0206: domBuilder = new DOMBuilder();
0207: user1NavigationManager.generateNavigationTree(domBuilder,
0208: new NavigationParams(navdoc1.getVariantKey(), null,
0209: false), null, false);
0210: navResult = domBuilder.getDocument();
0211: xpath = createXPath("/n:navigationTree/n:doc[1]/@id");
0212: assertEquals(String.valueOf(document3.getId()), xpath
0213: .stringValueOf(navResult));
0214: xpath = createXPath("/n:navigationTree/n:doc[1]/n:doc[1]/@id");
0215: assertEquals(String.valueOf(document3.getId()), xpath
0216: .stringValueOf(navResult));
0217: xpath = createXPath("/n:navigationTree/n:doc[1]/n:doc[2]/@id");
0218: assertEquals("", xpath.stringValueOf(navResult));
0219: xpath = createXPath("/n:navigationTree/n:group[1]/@label");
0220: assertEquals("My Group", xpath.stringValueOf(navResult));
0221: xpath = createXPath("count(/n:navigationTree/n:group[1]/n:group[2]/n:doc)");
0222: assertEquals(3, xpath.numberValueOf(navResult).intValue());
0223: xpath = createXPath("/n:navigationTree/n:group[1]/n:group[2]/n:doc[2]/@label");
0224: assertEquals("X", xpath.stringValueOf(navResult));
0225: xpath = createXPath("count(/n:navigationTree/n:group[1]/n:doc)");
0226: assertEquals(1, xpath.numberValueOf(navResult).intValue());
0227:
0228: //
0229: // Check behaviour with recursive imports
0230: //
0231: {
0232: Document navdoc3 = repository.createDocument("navdoc3",
0233: navigationType.getId());
0234: navdoc3.setPart("NavigationDescription", "text/xml",
0235: new byte[0]);
0236: navdoc3.save();
0237:
0238: Document navdoc4 = repository.createDocument("navdoc4",
0239: navigationType.getId());
0240: navdoc4.setPart("NavigationDescription", "text/xml",
0241: new byte[0]);
0242: navdoc4.save();
0243:
0244: Document navdoc5 = repository.createDocument("navdoc5",
0245: navigationType.getId());
0246: navdoc5.setPart("NavigationDescription", "text/xml",
0247: new byte[0]);
0248: navdoc5.save();
0249:
0250: // first test 3 -> 4 -> 4
0251: StringBuilder content3 = new StringBuilder();
0252: content3
0253: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0254: content3.append(" <d:import docId='" + navdoc4.getId()
0255: + "'/>");
0256: content3.append("</d:navigationTree>");
0257: navdoc3.setPart("NavigationDescription", "text/xml",
0258: content3.toString().getBytes("UTF-8"));
0259: navdoc3.save();
0260:
0261: StringBuilder content4 = new StringBuilder();
0262: content4
0263: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0264: content4.append(" <d:import docId='" + navdoc4.getId()
0265: + "'/>");
0266: content4.append("</d:navigationTree>");
0267: navdoc4.setPart("NavigationDescription", "text/xml",
0268: content4.toString().getBytes("UTF-8"));
0269: navdoc4.save();
0270:
0271: System.out
0272: .println("!!WARNING!! If the test will now hang it is because the detecting of recursive imports does not work.");
0273: domBuilder = new DOMBuilder();
0274: user1NavigationManager.generateNavigationTree(domBuilder,
0275: new NavigationParams(navdoc3.getVariantKey(), null,
0276: false), null, false);
0277: navResult = domBuilder.getDocument();
0278: xpath = createXPath("count(//n:error)");
0279: assertEquals(1, xpath.numberValueOf(navResult).intValue());
0280:
0281: // test 3 -> 4 -> 5 -> 3
0282: content4 = new StringBuilder();
0283: content4
0284: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0285: content4.append(" <d:import docId='" + navdoc5.getId()
0286: + "'/>");
0287: content4.append("</d:navigationTree>");
0288: navdoc4.setPart("NavigationDescription", "text/xml",
0289: content4.toString().getBytes("UTF-8"));
0290: navdoc4.save();
0291:
0292: StringBuilder content5 = new StringBuilder();
0293: content5
0294: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0295: content5.append(" <d:import docId='" + navdoc3.getId()
0296: + "'/>");
0297: content5.append("</d:navigationTree>");
0298: navdoc5.setPart("NavigationDescription", "text/xml",
0299: content5.toString().getBytes("UTF-8"));
0300: navdoc5.save();
0301:
0302: System.out
0303: .println("!!WARNING!! If the test will now hang it is because the detecting of recursive imports does not work.");
0304: domBuilder = new DOMBuilder();
0305: user1NavigationManager.generateNavigationTree(domBuilder,
0306: new NavigationParams(navdoc3.getVariantKey(), null,
0307: false), null, false);
0308: navResult = domBuilder.getDocument();
0309: xpath = createXPath("count(//n:error)");
0310: assertEquals(1, xpath.numberValueOf(navResult).intValue());
0311: }
0312:
0313: // test 6 -> 6
0314: {
0315: Document navdoc6 = repository.createDocument("navdoc6",
0316: navigationType.getId());
0317: navdoc6.setPart("NavigationDescription", "text/xml",
0318: new byte[0]);
0319: navdoc6.save();
0320:
0321: StringBuilder content6 = new StringBuilder();
0322: content6
0323: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0324: content6.append(" <d:import docId='" + navdoc6.getId()
0325: + "'/>");
0326: content6.append("</d:navigationTree>");
0327: navdoc6.setPart("NavigationDescription", "text/xml",
0328: content6.toString().getBytes("UTF-8"));
0329: navdoc6.save();
0330:
0331: System.out
0332: .println("!!WARNING!! If the test will now hang it is because the detecting of recursive imports does not work.");
0333: domBuilder = new DOMBuilder();
0334: user1NavigationManager.generateNavigationTree(domBuilder,
0335: new NavigationParams(navdoc6.getVariantKey(), null,
0336: false), null, false);
0337: navResult = domBuilder.getDocument();
0338: xpath = createXPath("count(//n:error)");
0339: assertEquals(1, xpath.numberValueOf(navResult).intValue());
0340: }
0341:
0342: //
0343: // Test access to navigation tree should fail when in 'last' versionmode and user has
0344: // no read access to the navigation tree
0345: //
0346: {
0347: Document navdoc = repository.createDocument("navdoc",
0348: navigationType.getId());
0349: StringBuilder content = new StringBuilder();
0350: content
0351: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0352: content.append("</d:navigationTree>");
0353: navdoc.setPart("NavigationDescription", "text/xml", content
0354: .toString().getBytes("UTF-8"));
0355: navdoc.setNewVersionState(VersionState.DRAFT);
0356: navdoc.save();
0357:
0358: domBuilder = new DOMBuilder();
0359: navigationManager.generateNavigationTree(domBuilder,
0360: new NavigationParams(navdoc.getVariantKey(),
0361: NavigationVersionMode.LAST, null, false),
0362: null, false);
0363: navResult = domBuilder.getDocument();
0364: xpath = createXPath("count(/n:navigationTree/*)");
0365: assertEquals(0, xpath.numberValueOf(navResult).intValue());
0366:
0367: domBuilder = new DOMBuilder();
0368: user1NavigationManager.generateNavigationTree(domBuilder,
0369: new NavigationParams(navdoc.getVariantKey(),
0370: NavigationVersionMode.LAST, null, false),
0371: null, false);
0372: navResult = domBuilder.getDocument();
0373: xpath = createXPath("count(/n:navigationTree/n:error)");
0374: assertEquals(1, xpath.numberValueOf(navResult).intValue());
0375: }
0376:
0377: // Dito when the tree one has no access to is imported
0378: {
0379: Document navdoc = repository.createDocument("navdoc",
0380: navigationType.getId());
0381: StringBuilder content = new StringBuilder();
0382: content
0383: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0384: content.append("</d:navigationTree>");
0385: navdoc.setPart("NavigationDescription", "text/xml", content
0386: .toString().getBytes("UTF-8"));
0387: navdoc.setNewVersionState(VersionState.DRAFT);
0388: navdoc.save();
0389:
0390: Document navdocB = repository.createDocument("navdoc",
0391: navigationType.getId());
0392: StringBuilder contentB = new StringBuilder();
0393: content
0394: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0395: content.append(" <d:import docId='")
0396: .append(navdoc.getId()).append("'/>");
0397: content.append("</d:navigationTree>");
0398: navdocB.setPart("NavigationDescription", "text/xml",
0399: contentB.toString().getBytes("UTF-8"));
0400: navdocB.save();
0401:
0402: domBuilder = new DOMBuilder();
0403: user1NavigationManager.generateNavigationTree(domBuilder,
0404: new NavigationParams(navdocB.getVariantKey(),
0405: NavigationVersionMode.LAST, null, false),
0406: null, false);
0407: navResult = domBuilder.getDocument();
0408: xpath = createXPath("count(/n:navigationTree/n:error)");
0409: assertEquals(1, xpath.numberValueOf(navResult).intValue());
0410:
0411: }
0412:
0413: //
0414: // Test cache invalidation on updates
0415: //
0416:
0417: // update to a document
0418: document3.setField(stringField.getId(), "Z");
0419: document3.save();
0420:
0421: domBuilder = new DOMBuilder();
0422: user1NavigationManager.generateNavigationTree(domBuilder,
0423: new NavigationParams(navdoc1.getVariantKey(), null,
0424: false), null, false);
0425: navResult = domBuilder.getDocument();
0426:
0427: xpath = createXPath("/n:navigationTree/n:group[1]/n:group[2]/n:doc[3]/@label");
0428: assertEquals("Z", xpath.stringValueOf(navResult));
0429:
0430: // update to a navigation tree itself
0431:
0432: // with current implementation it doesn't make a difference so skipped this for now
0433:
0434: //
0435: // Test navigationManager.lookup method
0436: //
0437:
0438: LookupAlternative[] lookupAlternatives = new LookupAlternative[] { new LookupAlternative(
0439: "x", collection1.getId(), navdoc1.getVariantKey()) };
0440:
0441: // a request for a group should give a redirect to its first document child
0442: NavigationLookupResult lookupResult = user1NavigationManager
0443: .lookup("/mygroup", -1, -1, lookupAlternatives);
0444: assertTrue(lookupResult.isRedirect());
0445: assertEquals("/mygroup/g1/3-DSYTEST", lookupResult
0446: .getNavigationPath());
0447:
0448: // a request for a non-existing path should give a not found response
0449: lookupResult = user1NavigationManager.lookup("/abc", -1, -1,
0450: lookupAlternatives);
0451: assertTrue(lookupResult.isNotFound());
0452:
0453: // a request for a document ID (does not matter whether the document really exists) that does not occur in the tree
0454: lookupResult = user1NavigationManager.lookup("/2323", -1, -1,
0455: lookupAlternatives);
0456: assertEquals("2323-DSYTEST", lookupResult.getVariantKey()
0457: .getDocumentId());
0458: assertEquals("", lookupResult.getNavigationPath());
0459:
0460: // a request for a document ID that does not occur at the given path but does occur
0461: // at another location in the tree
0462: lookupResult = user1NavigationManager.lookup("/1", -1, -1,
0463: lookupAlternatives);
0464: assertTrue(lookupResult.isRedirect());
0465: assertEquals("/3-DSYTEST/1-DSYTEST", lookupResult
0466: .getNavigationPath());
0467:
0468: // a request for a group node which does not have any document node child
0469: lookupResult = user1NavigationManager.lookup(
0470: "/mygroup/g1/emptygroup", -1, -1, lookupAlternatives);
0471: assertTrue(lookupResult.isNotFound());
0472:
0473: // a request for a link node
0474: lookupResult = user1NavigationManager.lookup("/l1", -1, -1,
0475: lookupAlternatives);
0476: assertTrue(lookupResult.isNotFound());
0477:
0478: // a request for a document node that exists at its given path (the most 'normal' situation)
0479: lookupResult = user1NavigationManager.lookup(
0480: "/3-DSYTEST/3-DSYTEST", -1, -1, lookupAlternatives);
0481: assertEquals("3-DSYTEST", lookupResult.getVariantKey()
0482: .getDocumentId());
0483: assertEquals("/3-DSYTEST/3-DSYTEST", lookupResult
0484: .getNavigationPath());
0485:
0486: //
0487: // Test contextualized trees
0488: //
0489:
0490: // Only activeDoc, not activePath specified
0491: domBuilder = new DOMBuilder();
0492: user1NavigationManager.generateNavigationTree(domBuilder,
0493: new NavigationParams(navdoc1.getVariantKey(), null,
0494: false), document3.getVariantKey(), false);
0495: navResult = domBuilder.getDocument();
0496:
0497: xpath = createXPath("/n:navigationTree/@selectedPath");
0498: assertEquals("/" + document3.getId(), xpath
0499: .stringValueOf(navResult));
0500:
0501: xpath = createXPath("/n:navigationTree/n:doc[1]/@selected");
0502: assertEquals("true", xpath.stringValueOf(navResult));
0503:
0504: xpath = createXPath("count(//*[@selected='true'])");
0505: assertEquals(1, xpath.numberValueOf(navResult).intValue());
0506:
0507: // activePath specified
0508: String activePath = "/" + document3.getId() + "/"
0509: + document3.getId();
0510: domBuilder = new DOMBuilder();
0511: user1NavigationManager.generateNavigationTree(domBuilder,
0512: new NavigationParams(navdoc1.getVariantKey(),
0513: activePath, false), document3.getVariantKey(),
0514: false);
0515: navResult = domBuilder.getDocument();
0516:
0517: xpath = createXPath("/n:navigationTree/@selectedPath");
0518: assertEquals(activePath, xpath.stringValueOf(navResult));
0519:
0520: xpath = createXPath("/n:navigationTree/n:doc[1]/n:doc[1]/@selected");
0521: assertEquals("true", xpath.stringValueOf(navResult));
0522:
0523: xpath = createXPath("count(//*[@selected='true'])");
0524:
0525: // activePath specified, with a group in the activePath
0526: assertEquals(2, xpath.numberValueOf(navResult).intValue());
0527: activePath = "/mygroup/g2/" + document3.getId();
0528: domBuilder = new DOMBuilder();
0529: user1NavigationManager.generateNavigationTree(domBuilder,
0530: new NavigationParams(navdoc1.getVariantKey(),
0531: activePath, false), document3.getVariantKey(),
0532: false);
0533: navResult = domBuilder.getDocument();
0534:
0535: xpath = createXPath("/n:navigationTree/@selectedPath");
0536: assertEquals(activePath, xpath.stringValueOf(navResult));
0537:
0538: xpath = createXPath("/n:navigationTree/n:group[1]/n:group[2]/@selected");
0539: assertEquals("true", xpath.stringValueOf(navResult));
0540:
0541: xpath = createXPath("count(//*[@selected='true'])");
0542: assertEquals(3, xpath.numberValueOf(navResult).intValue());
0543:
0544: //
0545: // Simple test of nested nodes in query, with useSelectValues=0
0546: // Includes some testing of the context value resolver (escape syntax)
0547: //
0548: {
0549: Document navdoc = repository.createDocument("navdoc",
0550: navigationType.getId());
0551: StringBuilder content = new StringBuilder();
0552: content
0553: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0554: content.append(" <d:query q='select name where id='"
0555: + document1.getId() + "' or id='"
0556: + document2.getId()
0557: + "' order by name' useSelectValues='0'>");
0558: content
0559: .append(" <d:doc id='${documentId}' branch='${branchId}' language='${languageId}' label='${1} boe'/>");
0560: content
0561: .append(" <d:link url='http://www.som$$\\${\\$ewhere?param=${1}${xyz}' label='${1}'/>");
0562: content.append(" </d:query>");
0563: content.append("</d:navigationTree>");
0564: navdoc.setPart("NavigationDescription", "text/xml", content
0565: .toString().getBytes("UTF-8"));
0566: navdoc.save();
0567:
0568: domBuilder = new DOMBuilder();
0569: navigationManager.generateNavigationTree(domBuilder,
0570: new NavigationParams(navdoc.getVariantKey(),
0571: NavigationVersionMode.LIVE, null, false),
0572: null, false);
0573: navResult = domBuilder.getDocument();
0574:
0575: xpath = createXPath("count(/n:navigationTree/n:doc)");
0576: assertEquals(2, xpath.numberValueOf(navResult).intValue());
0577:
0578: xpath = createXPath("/n:navigationTree/n:link[1]/@label");
0579: assertEquals("Document 1", xpath.stringValueOf(navResult));
0580:
0581: xpath = createXPath("/n:navigationTree/n:link[1]/@url");
0582: assertEquals(
0583: "http://www.som$$${\\$ewhere?param=Document+1${xyz}",
0584: xpath.stringValueOf(navResult));
0585: }
0586:
0587: // Test query which doesn't return anything
0588: {
0589: Document navdoc = repository.createDocument("navdoc",
0590: navigationType.getId());
0591: StringBuilder content = new StringBuilder();
0592: content
0593: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0594: content
0595: .append(" <d:query q='select name, ownerLogin where 1=2 order by name'>");
0596: content.append(" <d:column sortOrder='ascending'/>");
0597: content.append(" <d:column sortOrder='ascending'/>");
0598: content.append(" <d:link url='boe' label='boe'/>");
0599: content.append(" </d:query>");
0600: content.append("</d:navigationTree>");
0601: navdoc.setPart("NavigationDescription", "text/xml", content
0602: .toString().getBytes("UTF-8"));
0603: navdoc.save();
0604:
0605: domBuilder = new DOMBuilder();
0606: navigationManager.generateNavigationTree(domBuilder,
0607: new NavigationParams(navdoc.getVariantKey(),
0608: NavigationVersionMode.LIVE, null, false),
0609: null, false);
0610: navResult = domBuilder.getDocument();
0611:
0612: xpath = createXPath("count(/n:navigationTree/*)");
0613: assertEquals(0, xpath.numberValueOf(navResult).intValue());
0614: }
0615:
0616: //
0617: // Test nested queries
0618: //
0619: {
0620: Document navdoc = repository.createDocument("navdoc",
0621: navigationType.getId());
0622: StringBuilder content = new StringBuilder();
0623: content
0624: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0625: content
0626: .append(" <d:query q='select name, variantLastModified where true order by name' useSelectValues='0'>");
0627: content
0628: .append(" <d:doc id='${documentId}' branch='${branchId}' language='${languageId}' label='${1} ${2}'>");
0629: content
0630: .append(" <d:query q='select name, variantLastModified where variantLastModified=${2} order by name' useSelectValues='0'>");
0631: content
0632: .append(" <d:doc id='${documentId}' branch='${branchId}' language='${languageId}' label='${../1} ${2}'/>");
0633: content.append(" </d:query>");
0634: content.append(" </d:doc>");
0635: content.append(" </d:query>");
0636: content.append("</d:navigationTree>");
0637: navdoc.setPart("NavigationDescription", "text/xml", content
0638: .toString().getBytes("UTF-8"));
0639: navdoc.save();
0640:
0641: domBuilder = new DOMBuilder();
0642: navigationManager.generateNavigationTree(domBuilder,
0643: new NavigationParams(navdoc.getVariantKey(),
0644: NavigationVersionMode.LIVE, null, false),
0645: null, false);
0646: navResult = domBuilder.getDocument();
0647:
0648: xpath = createXPath("count(/n:navigationTree/n:doc)");
0649: assertTrue(xpath.numberValueOf(navResult).intValue() > 1);
0650:
0651: // The children of each document node should have the same label as its parent node
0652: xpath = createXPath("/n:navigationTree/n:doc");
0653: List docs = xpath.selectNodes(navResult);
0654: for (int i = 0; i < docs.size(); i++) {
0655: Element el = (Element) docs.get(i);
0656: String label = el.getAttribute("label");
0657: xpath = createXPath("n:doc");
0658: List subdocs = xpath.selectNodes(el);
0659: assertTrue(subdocs.size() > 0);
0660: for (int k = 0; k < subdocs.size(); k++) {
0661: assertEquals(label, ((Element) subdocs.get(k))
0662: .getAttribute("label"));
0663: }
0664: }
0665: }
0666:
0667: // Test useSelectValues != 0
0668: {
0669: Document navdoc = repository.createDocument("navdoc",
0670: navigationType.getId());
0671: StringBuilder content = new StringBuilder();
0672: content
0673: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0674: content
0675: .append(" <d:query q='select name, variantLastModified, ownerLogin where id='"
0676: + document1.getId()
0677: + "' order by name' useSelectValues='2'/>");
0678: content.append("</d:navigationTree>");
0679: navdoc.setPart("NavigationDescription", "text/xml", content
0680: .toString().getBytes("UTF-8"));
0681: navdoc.save();
0682:
0683: domBuilder = new DOMBuilder();
0684: navigationManager.generateNavigationTree(domBuilder,
0685: new NavigationParams(navdoc.getVariantKey(),
0686: NavigationVersionMode.LIVE, null, false),
0687: null, false);
0688: navResult = domBuilder.getDocument();
0689:
0690: xpath = createXPath("count(//n:doc)");
0691: assertEquals(1, xpath.numberValueOf(navResult).intValue());
0692:
0693: xpath = createXPath("count(//n:group)");
0694: assertEquals(1, xpath.numberValueOf(navResult).intValue());
0695: }
0696:
0697: //
0698: // Test tree with link fields, hierarchical fields, ...
0699: //
0700: {
0701: FieldType linkField = schema.createFieldType("LinkField",
0702: ValueType.LINK);
0703: linkField.save();
0704:
0705: FieldType hierMvField = schema.createFieldType(
0706: "HierMvField", ValueType.STRING, true, true);
0707: hierMvField.save();
0708:
0709: DocumentType documentType2 = schema
0710: .createDocumentType("doctype2");
0711: documentType2.addFieldType(linkField, false);
0712: documentType2.addFieldType(hierMvField, false);
0713: documentType2.save();
0714:
0715: Document document10 = repository.createDocument(
0716: "Document 10", documentType2.getId());
0717: document10.setField("LinkField", document1.getVariantKey());
0718: document10.setField("HierMvField",
0719: new Object[] { new HierarchyPath(new Object[] {
0720: "A", "B" }) });
0721: document10.save();
0722:
0723: Document document11 = repository.createDocument(
0724: "Document 11", documentType2.getId());
0725: document11.setField("LinkField", document1.getVariantKey());
0726: document11.setField("HierMvField",
0727: new Object[] { new HierarchyPath(new Object[] {
0728: "A", "B" }) });
0729: document11.save();
0730:
0731: Document document12 = repository.createDocument(
0732: "Document 12", documentType2.getId());
0733: document12.setField("LinkField", document1.getVariantKey());
0734: document12.setField("HierMvField",
0735: new Object[] { new HierarchyPath(
0736: new Object[] { "A" }) });
0737: document12.save();
0738:
0739: Document document13 = repository.createDocument(
0740: "Document 13", documentType2.getId());
0741: document13.setField("LinkField", document2.getVariantKey());
0742: document13.setField("HierMvField",
0743: new Object[] { new HierarchyPath(
0744: new Object[] { "B" }) });
0745: document13.save();
0746:
0747: // Test building navtree with link field in select clause
0748: {
0749: Document navdoc = repository.createDocument("navdoc",
0750: navigationType.getId());
0751: StringBuilder content = new StringBuilder();
0752: content
0753: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0754: content
0755: .append(" <d:query q='select $LinkField where documentType='doctype2' order by name'/>");
0756: content.append("</d:navigationTree>");
0757: navdoc.setPart("NavigationDescription", "text/xml",
0758: content.toString().getBytes("UTF-8"));
0759: navdoc.save();
0760:
0761: domBuilder = new DOMBuilder();
0762: navigationManager
0763: .generateNavigationTree(domBuilder,
0764: new NavigationParams(navdoc
0765: .getVariantKey(),
0766: NavigationVersionMode.LIVE,
0767: null, false), null, false);
0768: navResult = domBuilder.getDocument();
0769:
0770: // There's only 2 different values in the link fields, hence just 2 nodes
0771: xpath = createXPath("count(/n:navigationTree/n:doc)");
0772: assertEquals(2, xpath.numberValueOf(navResult)
0773: .intValue());
0774: }
0775:
0776: {
0777: Document navdoc = repository.createDocument("navdoc",
0778: navigationType.getId());
0779: StringBuilder content = new StringBuilder();
0780: content
0781: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0782: content
0783: .append(" <d:query q='select $LinkField, name where documentType='doctype2' order by name desc' useSelectValues='1'>");
0784: // more columns then needed, to test that (1) this doesn't confuse the implementation
0785: // and (2) that the nodes created by the child d:link element are NOT sorted according
0786: // to this but keep there original insertion order
0787: content.append(" <d:column sortOrder='ascending'/>");
0788: content.append(" <d:column sortOrder='ascending'/>");
0789: content.append(" <d:column sortOrder='ascending'/>");
0790: content.append(" <d:column sortOrder='ascending'/>");
0791: content.append(" <d:column sortOrder='ascending'/>");
0792: content.append(" <d:link url='boe' label='${2}'/>");
0793: content.append(" </d:query>");
0794: content.append("</d:navigationTree>");
0795: navdoc.setPart("NavigationDescription", "text/xml",
0796: content.toString().getBytes("UTF-8"));
0797: navdoc.save();
0798:
0799: domBuilder = new DOMBuilder();
0800: navigationManager
0801: .generateNavigationTree(domBuilder,
0802: new NavigationParams(navdoc
0803: .getVariantKey(),
0804: NavigationVersionMode.LIVE,
0805: null, false), null, false);
0806: navResult = domBuilder.getDocument();
0807:
0808: // The children of the query element are executed for each query resultset row, thus
0809: // even if there's only two distinct nodes created by the link field, there's 4 nested link nodes in total
0810: xpath = createXPath("count(/n:navigationTree/n:doc/n:link)");
0811: assertEquals(4, xpath.numberValueOf(navResult)
0812: .intValue());
0813:
0814: // Test sort order is maintained
0815: xpath = createXPath("/n:navigationTree/n:doc[1]/n:link[1]/@label");
0816: assertEquals("Document 12", xpath
0817: .stringValueOf(navResult));
0818:
0819: xpath = createXPath("/n:navigationTree/n:doc[1]/n:link[3]/@label");
0820: assertEquals("Document 10", xpath
0821: .stringValueOf(navResult));
0822: }
0823:
0824: {
0825: Document impnavdoc = repository.createDocument(
0826: "navdoc", navigationType.getId());
0827: StringBuilder impcontent = new StringBuilder();
0828: impcontent
0829: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0830: impcontent
0831: .append(" <d:link url='http://outerthought.org' label='Outerthought'/>");
0832: impcontent
0833: .append(" <d:link url='http://daisycms.org' label='Daisy CMS'/>");
0834: impcontent.append("</d:navigationTree>");
0835: impnavdoc.setPart("NavigationDescription", "text/xml",
0836: impcontent.toString().getBytes("UTF-8"));
0837: impnavdoc.save();
0838:
0839: Document navdoc = repository.createDocument("navdoc",
0840: navigationType.getId());
0841: StringBuilder content = new StringBuilder();
0842: content
0843: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0844: content
0845: .append(" <d:query q='select $LinkField, $HierMvField where documentType='doctype2' order by name'>");
0846: content.append(" <d:import docId='"
0847: + impnavdoc.getId() + "'/>");
0848: content.append(" </d:query>");
0849: content.append("</d:navigationTree>");
0850: navdoc.setPart("NavigationDescription", "text/xml",
0851: content.toString().getBytes("UTF-8"));
0852: navdoc.save();
0853:
0854: domBuilder = new DOMBuilder();
0855: navigationManager
0856: .generateNavigationTree(domBuilder,
0857: new NavigationParams(navdoc
0858: .getVariantKey(),
0859: NavigationVersionMode.LIVE,
0860: null, false), null, false);
0861: navResult = domBuilder.getDocument();
0862:
0863: xpath = createXPath("/n:navigationTree/n:doc[1]/n:group[1]/n:doc[1]/@documentId");
0864: assertEquals(document10.getId(), xpath
0865: .stringValueOf(navResult));
0866:
0867: xpath = createXPath("/n:navigationTree/n:doc[1]/n:group[1]/n:doc[1]/n:link[1]/@label");
0868: assertEquals("Outerthought", xpath
0869: .stringValueOf(navResult));
0870:
0871: xpath = createXPath("/n:navigationTree/n:doc[1]/n:group[1]/n:doc[1]/n:link[2]/@label");
0872: assertEquals("Daisy CMS", xpath
0873: .stringValueOf(navResult));
0874:
0875: xpath = createXPath("/n:navigationTree/n:doc[1]/n:doc[1]/@documentId");
0876: assertEquals(document12.getId(), xpath
0877: .stringValueOf(navResult));
0878:
0879: xpath = createXPath("/n:navigationTree/n:doc[2]/n:doc[1]/@documentId");
0880: assertEquals(document13.getId(), xpath
0881: .stringValueOf(navResult));
0882: }
0883: }
0884:
0885: //
0886: // Test separator node
0887: // especially removal of meaningless/disturbing separator nodes
0888: //
0889: {
0890: Document navdoc = repository.createDocument("navdoc",
0891: navigationType.getId());
0892: StringBuilder content = new StringBuilder();
0893: content
0894: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0895: content.append(" <d:separator/>");
0896: content.append(" <d:separator/>");
0897: content.append(" <d:separator/>");
0898: content.append(" <d:doc id='" + document1.getId() + "'/>");
0899: content.append(" <d:separator/>");
0900: content.append(" <d:separator/>");
0901: content.append(" <d:separator/>");
0902: content.append(" <d:doc id='999999999999'/>"); // a non-existing document
0903: content.append(" <d:separator/>");
0904: content.append(" <d:doc id='" + document2.getId() + "'/>");
0905: content.append(" <d:separator/>");
0906: content.append(" <d:separator/>");
0907: content.append(" <d:group label='boe'>"); // a group without visible children
0908: content.append(" <d:doc id='999999999999'/>"); // a non-existing document
0909: content.append(" </d:group>");
0910: content.append(" <d:separator/>");
0911: content.append(" <d:separator/>");
0912: content.append("</d:navigationTree>");
0913: navdoc.setPart("NavigationDescription", "text/xml", content
0914: .toString().getBytes("UTF-8"));
0915: navdoc.save();
0916:
0917: domBuilder = new DOMBuilder();
0918: navigationManager.generateNavigationTree(domBuilder,
0919: new NavigationParams(navdoc.getVariantKey(),
0920: NavigationVersionMode.LIVE, null, false),
0921: null, false);
0922: navResult = domBuilder.getDocument();
0923:
0924: // There should only be one separator between the two document nodes
0925: xpath = createXPath("count(/n:navigationTree/n:separator)");
0926: assertEquals(1, xpath.numberValueOf(navResult).intValue());
0927: }
0928:
0929: //
0930: // Test custom navigation depth
0931: //
0932: {
0933: Document navdoc = repository.createDocument("navdoc",
0934: navigationType.getId());
0935: StringBuilder content = new StringBuilder();
0936: content
0937: .append("<d:navigationTree xmlns:d='http://outerx.org/daisy/1.0#navigationspec'>");
0938: content.append(" <d:group id='group1' label='Group 1'>");
0939: content
0940: .append(" <d:doc id='" + document1.getId()
0941: + "'>");
0942: content.append(" <d:doc id='" + document1.getId()
0943: + "'>");
0944: content.append(" </d:doc>");
0945: content.append(" </d:doc>");
0946: content.append(" </d:group>");
0947: content.append(" <d:group id='group2' label='Group 2'>");
0948: content
0949: .append(" <d:doc id='" + document1.getId()
0950: + "'>");
0951: content.append(" <d:doc id='" + document1.getId()
0952: + "'>");
0953: content.append(" </d:doc>");
0954: content.append(" </d:doc>");
0955: content.append(" </d:group>");
0956: content.append(" <d:group id='group3' label='Group 3'>");
0957: content
0958: .append(" <d:doc id='" + document1.getId()
0959: + "'>");
0960: content.append(" <d:doc id='" + document1.getId()
0961: + "'>");
0962: content.append(" </d:doc>");
0963: content.append(" </d:doc>");
0964: content.append(" </d:group>");
0965: content.append("</d:navigationTree>");
0966: navdoc.setPart("NavigationDescription", "text/xml", content
0967: .toString().getBytes("UTF-8"));
0968: navdoc.save();
0969:
0970: domBuilder = new DOMBuilder();
0971: navigationManager.generateNavigationTree(domBuilder,
0972: new NavigationParams(navdoc.getVariantKey(),
0973: NavigationVersionMode.LIVE, "/group1/"
0974: + document1.getId() + "/"
0975: + document1.getId(), true, 2,
0976: Locale.US), null, false);
0977: navResult = domBuilder.getDocument();
0978:
0979: xpath = createXPath("count(/n:navigationTree/n:group[@id='group1']//n:doc)");
0980: assertEquals(2, xpath.numberValueOf(navResult).intValue());
0981:
0982: xpath = createXPath("count(/n:navigationTree/n:group[@id='group2']//n:doc)");
0983: assertEquals(1, xpath.numberValueOf(navResult).intValue());
0984:
0985: xpath = createXPath("count(/n:navigationTree/n:group[@id='group3']//n:doc)");
0986: assertEquals(1, xpath.numberValueOf(navResult).intValue());
0987:
0988: domBuilder = new DOMBuilder();
0989: navigationManager.generateNavigationTree(domBuilder,
0990: new NavigationParams(navdoc.getVariantKey(),
0991: NavigationVersionMode.LIVE, "/group1/"
0992: + document1.getId() + "/"
0993: + document1.getId(), false, 2,
0994: Locale.US), null, false);
0995: navResult = domBuilder.getDocument();
0996:
0997: xpath = createXPath("count(/n:navigationTree/n:group[@id='group1']//n:doc)");
0998: assertEquals(2, xpath.numberValueOf(navResult).intValue());
0999:
1000: xpath = createXPath("count(/n:navigationTree/n:group[@id='group2']//n:doc)");
1001: assertEquals(1, xpath.numberValueOf(navResult).intValue());
1002:
1003: xpath = createXPath("count(/n:navigationTree/n:group[@id='group3']//n:doc)");
1004: assertEquals(1, xpath.numberValueOf(navResult).intValue());
1005: }
1006: }
1007:
1008: private DOMXPath createXPath(String expr) throws Exception {
1009: DOMXPath xpath = new DOMXPath(expr);
1010: xpath.addNamespace("n",
1011: "http://outerx.org/daisy/1.0#navigation");
1012: return xpath;
1013: }
1014:
1015: private void dumpDOM(org.w3c.dom.Document document)
1016: throws Exception {
1017: // small dirty trick for pretty printing
1018: System.out
1019: .println(XmlObject.Factory.parse(document).toString());
1020: }
1021: }
|