001: /* Copyright 2005 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.layout.dlm;
007:
008: import java.io.StringWriter;
009: import java.util.Enumeration;
010: import java.util.HashSet;
011: import java.util.Set;
012: import java.util.Vector;
013:
014: import javax.xml.transform.Result;
015: import javax.xml.transform.Source;
016: import javax.xml.transform.Transformer;
017: import javax.xml.transform.TransformerFactory;
018: import javax.xml.transform.TransformerFactoryConfigurationError;
019: import javax.xml.transform.dom.DOMSource;
020: import javax.xml.transform.stream.StreamResult;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024: import org.jasig.portal.AuthorizationException;
025: import org.jasig.portal.EntityIdentifier;
026: import org.jasig.portal.security.IAuthorizationPrincipal;
027: import org.jasig.portal.security.IPerson;
028: import org.jasig.portal.services.AuthorizationService;
029: import org.jasig.portal.utils.DocumentFactory;
030: import org.w3c.dom.Document;
031: import org.w3c.dom.Element;
032: import org.w3c.dom.Node;
033:
034: /** Performs merging of layout fragments into a single document containing
035: * all incorporated layout fragment elements from the set of fragments
036: * passed in. This merge is trivial, appending all children of each
037: * fragment into the composite document and recording their identifiers
038: * in the document identifier cache. No changes are made to the source
039: * fragments passed in.
040: *
041: * @version $Revision: 36561 $ $Date: 2006-04-28 12:17:49 -0700 (Fri, 28 Apr 2006) $
042: * @since uPortal 2.5
043: */
044: public class ILFBuilder {
045: public static final String RCS_ID = "@(#) $Header$";
046: private static final Log LOG = LogFactory.getLog(ILFBuilder.class);
047:
048: public static Document constructILF(Document PLF, Vector sequence,
049: IPerson person)
050: throws javax.xml.parsers.ParserConfigurationException,
051: AuthorizationException {
052: if (LOG.isDebugEnabled()) {
053: LOG.debug("Constructing ILF for IPerson='" + person + "'");
054: }
055: // first construct the destination document and root element. The root
056: // element should be a complete copy of the PLF's root including its
057: // node identifier in the new document. This requires the use of
058: // the implementation class to set the identifier for that node
059: // in the document.
060:
061: Document result = DocumentFactory.getNewDocument();
062: Element plfLayout = PLF.getDocumentElement();
063: Element ilfLayout = (Element) result.importNode(plfLayout,
064: false);
065: result.appendChild(ilfLayout);
066: Element plfRoot = (Element) plfLayout.getFirstChild();
067: Element ilfRoot = (Element) result.importNode(plfRoot, false);
068: ilfLayout.appendChild(ilfRoot);
069:
070: if (ilfRoot.getAttribute(Constants.ATT_ID) != null)
071: ilfRoot.setIdAttribute(Constants.ATT_ID, true);
072:
073: // build the auth principal for determining if pushed channels can be
074: // used by this user
075: EntityIdentifier ei = person.getEntityIdentifier();
076: AuthorizationService authS = AuthorizationService.instance();
077: IAuthorizationPrincipal ap = authS.newPrincipal(ei.getKey(), ei
078: .getType());
079:
080: // now merge fragments one at a time into ILF document
081: Enumeration fragments = sequence.elements();
082:
083: while (fragments.hasMoreElements())
084: mergeFragment((Document) fragments.nextElement(), result,
085: ap);
086: return result;
087: }
088:
089: /**
090: * Passes the layout root of each of these documents to mergeChildren
091: * causing all children of newLayout to be merged into compositeLayout
092: * following merging protocal for distributed layout management.
093: * @throws AuthorizationException
094: **/
095: public static void mergeFragment(Document fragment,
096: Document composite, IAuthorizationPrincipal ap)
097: throws AuthorizationException {
098: Element fragmentLayout = fragment.getDocumentElement();
099: Element fragmentRoot = (Element) fragmentLayout.getFirstChild();
100: Element compositeLayout = composite.getDocumentElement();
101: Element compositeRoot = (Element) compositeLayout
102: .getFirstChild();
103: mergeChildren(fragmentRoot, compositeRoot, ap, new HashSet());
104: }
105:
106: /**
107: * @param source parent of children
108: * @param dest receiver of children
109: * @param ap User's authorization principal for determining if they can view a channel
110: * @param visitedNodes A Set of nodes from the source tree that have been visited to get to this node, used to ensure a loop doesn't exist in the source tree.
111: * @throws AuthorizationException
112: */
113: private static void mergeChildren(Element source, Element dest,
114: IAuthorizationPrincipal ap, Set visitedNodes)
115: throws AuthorizationException {
116: //Record this node in the visited nodes set. If add returns false a loop has been detected
117: if (!visitedNodes.add(source)) {
118: final String msg = "mergeChildren has encountered a loop in the source DOM. currentNode='"
119: + source
120: + "', currentDepth='"
121: + visitedNodes.size()
122: + "', visitedNodes='"
123: + visitedNodes + "'";
124: final IllegalStateException ise = new IllegalStateException(
125: msg);
126: LOG.error(msg, ise);
127:
128: printNodeToDebug(source, "Source");
129: printNodeToDebug(dest, "Dest");
130:
131: throw ise;
132: }
133:
134: Document destDoc = dest.getOwnerDocument();
135:
136: Node item = source.getFirstChild();
137: while (item != null) {
138: if (item instanceof Element) {
139:
140: Element child = (Element) item;
141: Element newChild = null;
142:
143: if (null != child && mergeAllowed(child, ap)) {
144: newChild = (Element) destDoc.importNode(child,
145: false);
146: dest.appendChild(newChild);
147: String id = newChild.getAttribute(Constants.ATT_ID);
148: if (id != null && !id.equals(""))
149: newChild.setIdAttribute(Constants.ATT_ID, true);
150: mergeChildren(child, newChild, ap, visitedNodes);
151: }
152: }
153:
154: item = item.getNextSibling();
155: }
156:
157: //Remove this node from the visited nodes set
158: visitedNodes.remove(source);
159: }
160:
161: /**
162: * Tests to see if channels to be merged from ILF can be rendered by the
163: * end user. If not then they are discarded from the merge.
164: *
165: * @param child
166: * @param person
167: * @return
168: * @throws AuthorizationException
169: * @throws NumberFormatException
170: */
171: private static boolean mergeAllowed(Element child,
172: IAuthorizationPrincipal ap) throws AuthorizationException {
173: if (!child.getTagName().equals("channel"))
174: return true;
175:
176: String channelPublishId = child.getAttribute("chanID");
177: return ap.canRender(Integer.parseInt(channelPublishId));
178: }
179:
180: private static void printNodeToDebug(Node n, String name)
181: throws TransformerFactoryConfigurationError {
182: if (!LOG.isDebugEnabled()) {
183: return;
184: }
185:
186: final StringWriter writer = new StringWriter();
187:
188: try {
189: final TransformerFactory transFactory = TransformerFactory
190: .newInstance();
191: final Transformer trans = transFactory.newTransformer();
192:
193: final Source xmlSource = new DOMSource(n);
194:
195: final Result transResult = new StreamResult(writer);
196: trans.transform(xmlSource, transResult);
197:
198: final String xmlStr = writer.toString();
199: LOG.debug(name + " DOM Tree:\n\n" + xmlStr);
200: } catch (Exception e) {
201: LOG.error("Error printing out " + name + " DOM Tree", e);
202: final String xmlStr = writer.toString();
203: LOG.debug("Partial " + name + " DOM Tree:\n\n" + xmlStr);
204: }
205: }
206: }
|