Java tutorial
/* * Copyright (c) 2000-2004 Netspective Communications LLC. All rights reserved. * * Netspective Communications LLC ("Netspective") permits redistribution, modification and use of this file in source * and binary form ("The Software") under the Netspective Source License ("NSL" or "The License"). The following * conditions are provided as a summary of the NSL but the NSL remains the canonical license and must be accepted * before using The Software. Any use of The Software indicates agreement with the NSL. * * 1. Each copy or derived work of The Software must preserve the copyright notice and this notice unmodified. * * 2. Redistribution of The Software is allowed in object code form only (as Java .class files or a .jar file * containing the .class files) and only as part of an application that uses The Software as part of its primary * functionality. No distribution of the package is allowed as part of a software development kit, other library, * or development tool without written consent of Netspective. Any modified form of The Software is bound by these * same restrictions. * * 3. Redistributions of The Software in any form must include an unmodified copy of The License, normally in a plain * ASCII text file unless otherwise agreed to, in writing, by Netspective. * * 4. The names "Netspective", "Axiom", "Commons", "Junxion", and "Sparx" are trademarks of Netspective and may not be * used to endorse or appear in products derived from The Software without written consent of Netspective. * * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED REPRESENTATIONS AND * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, * ARE HEREBY DISCLAIMED. * * NETSPECTIVE AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE OR ANY THIRD PARTY AS A * RESULT OF USING OR DISTRIBUTING THE SOFTWARE. IN NO EVENT WILL NETSPECTIVE OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN * IF IT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. */ package com.netspective.sparx.navigate; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.netspective.sparx.navigate.listener.NavigationPathFinalizeContentsListener; import com.netspective.sparx.navigate.listener.NavigationPathListener; import com.netspective.sparx.navigate.listener.NavigationPathListenerPlaceholder; import com.netspective.sparx.navigate.listener.NavigationPathMakeStateChangesListener; public class NavigationPath { public static final int INHERIT_PATH_FLAGS_FROM_PARENT = 0; static public final String PATH_SEPARATOR = "/"; public class State { private NavigationPathFlags flags; public State() { this.flags = (NavigationPathFlags) NavigationPath.this.getFlags().cloneFlags(); this.flags.setStateFlags(true); } public NavigationPathFlags getFlags() { return flags; } public NavigationPath getPath() { return NavigationPath.this; } } private Log log = LogFactory.getLog(NavigationPath.class); private Log securityLog; private NavigationTree owner; private NavigationPath parent; private NavigationPathFlags flags; private String qualifiedName; private String name; private List childrenList = new ArrayList(); private Map childrenMap = new HashMap(); private Map descendantsByQualifiedName = new HashMap(); private NavigationConditionalActions conditionalActions = new NavigationConditionalActions(); private Map ancestorMap = new HashMap(); private List ancestorsList = new ArrayList(); private List finalizeContentsListeners = new ArrayList(); private List makeStateChangesListeners = new ArrayList(); private NavigationPath defaultChild; private boolean defaultChildOfParent; private int maxChildLevel; private int level; private Map attributes = new HashMap(); public NavigationPath(NavigationTree owner) { setOwner(owner); flags = createFlags(); } public Log getLog() { return log; } public Log getSecurityLog() { return securityLog; } public Object getAttribute(String attributeId) { return attributes.get(attributeId); } public void removeAttribute(String attributeId) { attributes.remove(attributeId); } public void setAttribute(String attributeId, Object attributeValue) { attributes.put(attributeId, attributeValue); } /** * Calculate the absolute path of the given relativePath assuming it was relative to this path. * * @param relativePath The relative path to use * * @return The absolute path of the relative path based on this path */ public String getAbsPathRelativeToThisPath(String relativePath) { StringBuffer result = new StringBuffer(getQualifiedName()); if (!(result.charAt(result.length() - 1) == '/')) result.append('/'); if (relativePath.startsWith("/")) result.append(relativePath.substring(1)); else result.append(relativePath); return result.toString(); } public int size() { int result = 1; // start with self for (int i = 0; i < childrenList.size(); i++) result += ((NavigationPath) childrenList.get(i)).size(); return result; } public NavigationPathListener createListener() { return new NavigationPathListenerPlaceholder(); } public void addListener(NavigationPathListener listener) { if (listener instanceof NavigationPathFinalizeContentsListener) finalizeContentsListeners.add(listener); else if (listener instanceof NavigationPathMakeStateChangesListener) makeStateChangesListeners.add(listener); } public void finalizeContents() { // we now know our position in the tree to set the name for good setQualifiedName(constructQualifiedName()); for (int i = 0; i < childrenList.size(); i++) { final NavigationPath navigationPath = (NavigationPath) childrenList.get(i); navigationPath.registerChild(navigationPath); } for (int i = 0; i < childrenList.size(); i++) { final NavigationPath navigationPath = (NavigationPath) childrenList.get(i); navigationPath.finalizeContents(); } for (int i = 0; i < finalizeContentsListeners.size(); i++) ((NavigationPathFinalizeContentsListener) finalizeContentsListeners.get(i)) .finalizeNavigationPathContents(this); final String logName = getClass().getName() + "." + (getOwner().getName() + getQualifiedName()).replace('/', '.'); log = LogFactory.getLog(logName); securityLog = LogFactory.getLog("security." + logName); } public void makeStateChanges(NavigationContext nc) { //The make state changes should affect the current navPath, its sibilings, its ancestors and the ancestor's sibilings and its children if (getFlags().flagIsSet(NavigationPathFlags.HAS_CONDITIONAL_ACTIONS)) applyConditionals(conditionalActions.getActions(), nc); List sibilings = this.getSibilingList(); for (int i = 0; sibilings != null && i < sibilings.size(); i++) { NavigationPath sibiling = (NavigationPath) sibilings.get(i); if (sibiling.getFlags().flagIsSet(NavigationPathFlags.HAS_CONDITIONAL_ACTIONS)) applyConditionals(sibiling.getConditionals().getActions(), nc); } List ancestors = this.getAncestorsList(); for (int i = 0; ancestors != null && i < ancestors.size(); i++) { NavigationPath ancestor = (NavigationPath) ancestors.get(i); if (ancestor.getFlags().flagIsSet(NavigationPathFlags.HAS_CONDITIONAL_ACTIONS)) applyConditionals(ancestor.getConditionals().getActions(), nc); List ancestorSibilings = ancestor.getSibilingList(); for (int j = 0; ancestorSibilings != null && j < ancestorSibilings.size(); j++) { NavigationPath ancestorSibiling = (NavigationPath) ancestorSibilings.get(j); if (ancestorSibiling.getFlags().flagIsSet(NavigationPathFlags.HAS_CONDITIONAL_ACTIONS)) applyConditionals(ancestorSibiling.getConditionals().getActions(), nc); } } List children = this.getChildrenList(); for (int i = 0; children != null && i < children.size(); i++) { NavigationPath child = (NavigationPath) children.get(i); if (child.getFlags().flagIsSet(NavigationPathFlags.HAS_CONDITIONAL_ACTIONS)) applyConditionals(child.getConditionals().getActions(), nc); } for (int i = 0; i < makeStateChangesListeners.size(); i++) ((NavigationPathMakeStateChangesListener) makeStateChangesListeners.get(i)) .makeNavigationPathStateChanges(this, nc); } public State constructState() { return new State(); } public String getQualifiedName() { if (qualifiedName == null) return constructQualifiedName(); else return qualifiedName; } protected String constructQualifiedName() { StringBuffer sb = new StringBuffer(); if (parent != null) sb.append(parent.getQualifiedName()); if (sb.length() == 0 || sb.charAt(sb.length() - 1) != '/') sb.append(PATH_SEPARATOR); sb.append(getName()); return sb.toString(); } public String getQualifiedNameIncludingTreeId() { return "/" + owner.getName() + getQualifiedName(); } public void setQualifiedName(String qName) { qualifiedName = qName; } public void setName(String name) { this.name = name; } public String getName() { return name; } public NavigationTree getOwner() { return owner; } protected void setOwner(NavigationTree value) { owner = value; } public NavigationPath getParent() { return parent; } public void setParent(NavigationPath value) { if (value != this) { parent = value; setLevel(parent.getLevel() + 1); if (defaultChildOfParent) parent.setDefaultChild(this); generateAncestorList(); // in case we got added to our parent after our children were already added to us, recompute the // parents/ancestors for our children -- this will happen if a <page type="xxx"> template is used for a page List children = getChildrenList(); if (children != null) { for (int i = 0; i < children.size(); i++) ((NavigationPage) children.get(i)).setParent(this); } } else parent = null; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; setMaxChildLevel(level); for (NavigationPath activeParent = getParent(); activeParent != null;) { activeParent.setMaxChildLevel(level); activeParent = activeParent.getParent(); } } public int getMaxChildLevel() { return maxChildLevel; } public void setMaxChildLevel(int maxChildLevel) { if (maxChildLevel > this.maxChildLevel) this.maxChildLevel = maxChildLevel; owner.setMaxLevel(level); } /** * Get a list of conditional actions * * @return List a list of conditional actions */ public NavigationConditionalActions getConditionals() { return conditionalActions; } public void addConditional(NavigationConditionalAction action) { conditionalActions.addAction(action); getFlags().setFlag(NavigationPathFlags.HAS_CONDITIONAL_ACTIONS); } public NavigationConditionalAction createConditional() { return new NavigationConditionalAction(this); } public NavigationConditionalAction createConditional(Class cls) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { if (NavigationConditionalAction.class.isAssignableFrom(cls)) { Constructor c = cls.getConstructor(new Class[] { NavigationPath.class }); return (NavigationConditionalAction) c.newInstance(new Object[] { this }); } else throw new RuntimeException("Don't know what to do with with class: " + cls); } public void applyConditionals(List conditionals, NavigationContext nc) { if (conditionals != null) { for (int i = 0; i < conditionals.size(); i++) { NavigationConditionalAction action = (NavigationConditionalAction) conditionals.get(i); action.execute(nc); } } } public NavigationPathFlags createFlags() { return new NavigationPathFlags(); } public NavigationPathFlags getFlags() { return flags; } public void setFlags(NavigationPathFlags flags) { this.flags.copy(flags); } public void setFlagRecursively(long flag) { //flags.setFlag(flag); if (childrenList.size() > 0) { Iterator i = childrenList.iterator(); while (i.hasNext()) ((NavigationPath) i.next()).getFlags().setFlag(flag); } } public void clearFlagRecursively(long flag) { //flags.clearFlag(flag); if (childrenList.size() > 0) { Iterator i = childrenList.iterator(); while (i.hasNext()) ((NavigationPath) i.next()).getFlags().clearFlag(flag); } } /** * Returns the Map that contains all of its sibilings including itself. It is basically obtained by getting a * reference to the parent and then get a map of all of its children. * * @return Map A map object containing NavigationPath objects that represent the sibilings of the current object. */ public Map getSibilingMap() { if (parent != null) return parent.getChildrenMap(); return null; } /** * Returns the List that contains all of its sibilings including itself. It is basically obtained by getting a * reference to the parent and then get a list of all of its children. * * @return List A list object containing NavigationPath objects that represent the sibilings of the current object. */ public List getSibilingList() { if (parent != null) return parent.getChildrenList(); return null; } public void registerChild(NavigationPath path) { descendantsByQualifiedName.put(path.getQualifiedName(), path); if (parent != null) parent.registerChild(path); owner.register(path); } public void unregisterChild(NavigationPath path) { descendantsByQualifiedName.remove(path.getQualifiedName()); if (parent != null) parent.unregisterChild(path); owner.unregister(path); } public void appendChild(NavigationPath path) { path.setParent(this); childrenList.add(path); childrenMap.put(path.getName(), path); // registerChild(path); -- we do this in finalizeContents() } public void removeChild(NavigationPath path) { childrenList.remove(path); childrenMap.remove(path.getName()); unregisterChild(path); } public void removeAllChildren() { if (childrenList.size() == 0) return; NavigationPath[] children = (NavigationPath[]) childrenList .toArray(new NavigationPath[childrenList.size()]); for (int i = 0; i < children.length; i++) removeChild(children[i]); } /** * Get a child by its ID * * @return NavigationPath */ public NavigationPath getChildByName(String id) { return (NavigationPath) childrenMap.get(id); } public List getChildrenList() { return childrenList; } public Map getChildrenMap() { return childrenMap; } public NavigationPath getDefaultChild() { return defaultChild; } public void setDefaultChild(NavigationPath defaultChild) { this.defaultChild = defaultChild; } public boolean isDefault() { return defaultChildOfParent; } public void setDefault(boolean defaultChildOfParent) { this.defaultChildOfParent = defaultChildOfParent; if (defaultChildOfParent && getParent() != null) getParent().setDefaultChild(this); } public Map getAncestorMap() { return ancestorMap; } public void setAncestorMap(Map ancestorMap) { this.ancestorMap = ancestorMap; } public List getAncestorsList() { return ancestorsList; } public void setAncestorsList(List ancestorsList) { this.ancestorsList = ancestorsList; } /** * Populates the ancestorMap and ancestorList. It looks at its parent and adds a refence to it in the collections. * It then keeps doing that, but now looking at its parent's parent until it returns null. It then reverses the list * to maintain a 0=top structure. */ protected void generateAncestorList() { NavigationPath activePath = this.getParent(); if (getParent() != null) { List ancestorListReversed = new ArrayList(); Map ancestorMap = new HashMap(); while (activePath != null) { ancestorMap.put(activePath.getQualifiedName(), activePath); ancestorListReversed.add(activePath); activePath = activePath.getParent(); } setAncestorMap(ancestorMap); List ancestorList = new ArrayList(); for (int i = ancestorListReversed.size() - 1; i >= 0; i--) { NavigationPath NavigationPath = (NavigationPath) ancestorListReversed.get(i); ancestorList.add(NavigationPath); } setAncestorsList(ancestorList); } } public String toString() { StringBuffer html = new StringBuffer(); int atLevel = getLevel(); for (int i = 0; i < atLevel; i++) html.append(" "); html.append(getQualifiedName() + ": level " + getLevel() + " (max " + getMaxChildLevel() + "), class: " + getClass().getName() + "\n"); if (childrenList != null && childrenList.size() > 0) { Iterator i = childrenList.iterator(); while (i.hasNext()) { NavigationPath path = (NavigationPath) i.next(); html.append(path.toString()); } } return html.toString(); } }