/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use * this file except in compliance with the License. You may obtain a copy of the License at the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package org.apereo.portal.layout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * This class mimics the behaviour of XPath expression '/layout/folder/folder[@ID=$nodeId or * descendant::node()[@ID=$nodeId]]/@ID' - it searches for identifier of tab that contains a node * with given identifier. The rationale behind this method is because it is much faster using DOM * introspection (~0.1ms), than XPath queries (0-60ms <a * href="http://stackoverflow.com/questions/6340802/java-xpath-apache-jaxp-implementation-performance"> * without optimization</a>). * */ public class PortletTabIdResolver implements INodeIdResolver { protected final Logger logger = LoggerFactory.getLogger(getClass()); private final String layoutNodeId; public PortletTabIdResolver(String layoutNodeId) { this.layoutNodeId = layoutNodeId; } @Override public String traverseDocument(Document document) { // '/layout' - layouts for (Node root = document.getFirstChild(); root != null; root = root.getNextSibling()) { // '/layout/folder' - root/header/footer folders for (Node rootFolder = root.getFirstChild(); rootFolder != null; rootFolder = rootFolder.getNextSibling()) { // '/layout/folder/folder' - tabs for (Node tab = rootFolder.getFirstChild(); tab != null; tab = tab.getNextSibling()) { if (containsElmentWithId(tab, layoutNodeId)) { String foundId = ((Element) tab).getAttribute("ID"); logger.trace( "Found id {} after traversing document {} looking for layoutNodeId {}", foundId, document, layoutNodeId); return foundId; } } } } logger.trace("Did not find layoutNodeId {} in Document {}", layoutNodeId, document); return null; } /** * Recursevly find out whether node contains a folder or channel with given identifier. * * @param node Where to search. * @param id Identifier to search for. * @return true if node or any of its descendats contain an element with given identifier, false * otherwise. */ private boolean containsElmentWithId(Node node, String id) { String nodeName = node.getNodeName(); if ("channel".equals(nodeName) || "folder".equals(nodeName)) { Element e = (Element) node; if (id.equals(e.getAttribute("ID"))) { return true; } if ("folder".equals(nodeName)) { for (Node child = e.getFirstChild(); child != null; child = child.getNextSibling()) { if (containsElmentWithId(child, id)) { return true; } } } } return false; } }