001: /*
002: * Copyright (c) 1998-2000 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: *
028: * $Id: Navigation.java,v 1.2 2004/09/29 00:13:49 cvs Exp $
029: */
030:
031: package com.caucho.web;
032:
033: import com.caucho.util.Tree;
034: import com.caucho.vfs.Path;
035: import com.caucho.xml.LooseXml;
036: import com.caucho.xpath.Env;
037: import com.caucho.xpath.Expr;
038: import com.caucho.xpath.XPath;
039: import com.caucho.xpath.XPathFun;
040:
041: import org.w3c.dom.Document;
042: import org.w3c.dom.Element;
043: import org.w3c.dom.Node;
044:
045: import java.util.ArrayList;
046: import java.util.Iterator;
047:
048: public class Navigation {
049: private Element root;
050: private String base;
051: private Tree tree;
052:
053: public Navigation() {
054: }
055:
056: public Navigation(Path path, String base) throws Exception {
057: Document doc = new LooseXml().parseDocument(path);
058:
059: init(doc.getDocumentElement(), base);
060: }
061:
062: public Navigation(Env env, Path path, String base) throws Exception {
063: Document doc = new LooseXml().parseDocument(path);
064:
065: init(env, doc.getDocumentElement(), base);
066: }
067:
068: /**
069: * Create a new navigation structure.
070: *
071: * @param root the top of the navigation
072: */
073: public Navigation(Env env, Element root, String base)
074: throws Exception {
075: init(env, root, base);
076: }
077:
078: public Navigation(Element root, String base) throws Exception {
079: init(root, base);
080: }
081:
082: public void init(Element root, String base) throws Exception {
083: init(null, root, base);
084: }
085:
086: public void init(Env env, Element root, String base)
087: throws Exception {
088: tree = new Tree(null);
089:
090: this .root = root;
091: if (base == null || base == "")
092: base = "/";
093:
094: this .base = base;
095:
096: if (root != null)
097: fillChildren(env, tree, root.getFirstChild(), base);
098: }
099:
100: public static Navigation createNested(Path pwd, String base)
101: throws Exception {
102: return createNested(null, pwd, base);
103: }
104:
105: public static Navigation createNested(Env env, Path pwd, String base)
106: throws Exception {
107: Navigation baseNav = null;
108: Navigation subNav = null;
109:
110: if (base.startsWith("/"))
111: base = base.substring(1);
112:
113: String dir = base;
114: while (true) {
115: Path path = pwd.lookup(dir).lookup("toc.xml");
116:
117: Navigation nav = null;
118:
119: if (path.exists())
120: nav = new Navigation(env, path, dir);
121:
122: if (baseNav == null)
123: baseNav = nav;
124: else if (nav != null)
125: baseNav.linkParent(nav);
126:
127: if (dir.equals(""))
128: break;
129:
130: int p;
131: if (dir.endsWith("/")) {
132: p = dir.lastIndexOf('/', dir.length() - 2);
133: } else
134: p = dir.lastIndexOf('/');
135:
136: if (p <= 0)
137: dir = "";
138: else
139: dir = dir.substring(0, p + 1);
140: }
141:
142: return baseNav;
143: }
144:
145: public static Navigation createNested(ArrayList paths, String base)
146: throws Exception {
147: return createNested(null, paths, base);
148: }
149:
150: public static Navigation createNested(Env env, ArrayList paths,
151: String base) throws Exception {
152: Navigation baseNav = null;
153: Navigation subNav = null;
154:
155: if (base.startsWith("/"))
156: base = base.substring(1);
157:
158: String dir = base;
159: for (int i = 0; i < paths.size(); i++) {
160: Path path = ((Path) paths.get(i)).lookup("toc.xml");
161:
162: Navigation nav = null;
163:
164: if (path.exists())
165: nav = new Navigation(env, path, dir);
166:
167: if (baseNav == null)
168: baseNav = nav;
169: else if (nav != null)
170: baseNav.linkParent(nav);
171:
172: if (dir.equals(""))
173: break;
174:
175: int p;
176: if (dir.endsWith("/")) {
177: p = dir.lastIndexOf('/', dir.length() - 2);
178: } else
179: p = dir.lastIndexOf('/');
180:
181: if (p <= 0)
182: dir = "";
183: else
184: dir = dir.substring(0, p + 1);
185: }
186:
187: return baseNav;
188: }
189:
190: public Navigation linkParent(Navigation parent) {
191: if (tree == null) {
192: tree = parent.tree;
193: return this ;
194: }
195:
196: if (tree.getFirst() == null)
197: return this ;
198:
199: NavItem test = (NavItem) tree.getFirst().getData();
200: NavItem link = parent.findURL(test.getLink());
201:
202: if (link == null)
203: return null;
204:
205: Tree parentTree = link.getTree();
206: linkTree(link.getTree(), tree.getFirst());
207: this .tree = parent.tree;
208:
209: return this ;
210: }
211:
212: /**
213: * Attaches the child tree in its proper location in the dest tree
214: *
215: * @param destTree parent tree
216: * @param subTree child tree
217: */
218: private void linkTree(Tree destTree, Tree subTree) {
219: for (Tree child = subTree.getFirst(); child != null; child = child
220: .getNext()) {
221: NavItem item = (NavItem) child.getData();
222: Tree childTree = destTree.append(item);
223: item.setTree(childTree);
224: linkTree(childTree, child);
225: }
226: }
227:
228: /**
229: * Returns an attribute from the top-level navigation element.
230: *
231: * @param name The name of the attribute.
232: */
233: public String getAttribute(String name) {
234: if (root == null)
235: return "";
236:
237: return root.getAttribute(name);
238: }
239:
240: /**
241: * @param url the url to match
242: */
243: public NavItem findURL(String url) {
244: if (tree == null)
245: return null;
246:
247: url = normalizeURL(url);
248:
249: Iterator iter = tree.dfs();
250: while (iter.hasNext()) {
251: Tree tree = (Tree) iter.next();
252: NavItem item = (NavItem) tree.getData();
253:
254: if (item.getLink().equals(url)) {
255: return item;
256: }
257: }
258:
259: return null;
260: }
261:
262: /**
263: */
264: private void fillChildren(Env env, Tree tree, Node childNode,
265: String base) throws Exception {
266: XPathFun docShouldDisplay = env == null ? null : env
267: .getFunction("doc-should-display");
268:
269: for (; childNode != null; childNode = childNode
270: .getNextSibling()) {
271: if (!childNode.getNodeName().equals("item"))
272: continue;
273:
274: if (docShouldDisplay != null
275: && !Expr.toBoolean(docShouldDisplay.eval(childNode,
276: env, null, null)))
277: continue;
278:
279: Element elt = (Element) childNode;
280:
281: NavItem item = new NavItem();
282: String href = linkPattern.evalString(elt);
283:
284: item.setLink(resolveURL(href, childNode, base));
285: item.setTitle(titlePattern.evalString(elt));
286:
287: String desc;
288: desc = descPattern.evalString(elt);
289: item.setDescription(desc);
290:
291: item.setProduct(_productPattern.evalString(elt));
292:
293: Tree childTree = tree.append(item);
294: item.setTree(childTree);
295:
296: fillChildren(env, childTree, childNode.getFirstChild(),
297: base);
298: }
299: }
300:
301: private String resolveURL(String url, Node node, String base) {
302: if (url.length() == 0)
303: return "/";
304:
305: if (url.startsWith("http:") || url.charAt(0) == '/')
306: return url; // normalizeURL(url);
307:
308: for (; node instanceof Element; node = node.getParentNode()) {
309: Element elt = (Element) node;
310:
311: String nodeBase = elt.getAttribute("xml:base");
312:
313: if (nodeBase.equals(""))
314: continue;
315:
316: if (!nodeBase.endsWith("/"))
317: return resolveURL(nodeBase + "/" + url, elt
318: .getParentNode(), base);
319: else
320: return resolveURL(nodeBase + url, elt.getParentNode(),
321: base);
322: }
323:
324: if (!base.endsWith("/"))
325: return normalizeURL(base + "/" + url);
326: else
327: return normalizeURL(base + url);
328: }
329:
330: private String normalizeURL(String url) {
331: if (url.startsWith("/"))
332: return url;
333: else if (url.startsWith("http://"))
334: return url;
335: else
336: return "/" + url;
337:
338: /*
339: int i;
340: for (i = "http://".length(); i < url.length(); i++) {
341: if (url.charAt(i) == '/') {
342: return url.substring(i);
343: }
344: }
345:
346: return "/";
347: */
348: }
349:
350: static Expr linkPattern;
351: static Expr titlePattern;
352: static Expr descPattern;
353: static Expr _productPattern;
354: static {
355: try {
356: linkPattern = XPath.parseExpr("if(@link,@link,link)");
357: titlePattern = XPath.parseExpr("if(@title,@title,title)");
358: descPattern = XPath
359: .parseExpr("if(@description,@description,description)");
360: _productPattern = XPath
361: .parseExpr("if(@product,@product,product)");
362: } catch (Exception e) {
363: }
364: }
365: }
|