001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kew.xml; 017 018import org.apache.commons.lang.StringEscapeUtils; 019import org.kuali.rice.core.api.util.ConcreteKeyValue; 020import org.kuali.rice.core.api.util.KeyValue; 021import org.xml.sax.Attributes; 022import org.xml.sax.SAXException; 023import org.xml.sax.helpers.XMLFilterImpl; 024 025import java.util.ArrayList; 026import java.util.List; 027 028/** 029 * This abstract class handles the xml stack of elements and makes 030 * it easier to run a transformation on certain elements. 031 * 032 * @author Kuali Rice Team (rice.collab@kuali.org) 033 * 034 */ 035public abstract class AbstractTransformationFilter extends XMLFilterImpl { 036 037 038 // The list which helps keep track of where we are in the XML 039 // hierarchy as the stream is being processed 040 private List<String> groupXmlStack = new ArrayList<String>(); 041 042 /** 043 * 044 * This method allows you to modify the element passed in. The returned element 045 * will be pushed into a "super.startElement(uri, localName, qName, atts)" call. 046 * 047 * @param currentElement 048 * @return 049 */ 050 public abstract CurrentElement transformStartElement(CurrentElement currentElement) throws SAXException; 051 052 /** 053 * 054 * This method allows you to modify the element passed in. The returned element 055 * will be pushed into a "super.endElement(uri, localName, qName" call. 056 * 057 * @param currentElement 058 * @return 059 */ 060 public abstract CurrentElement transformEndElement(CurrentElement currentElement) throws SAXException; 061 062 /* 063 * Build a Map that maps elements we intend to transform to their corresponding transformed value. 064 * The keys in this Map are "hierarchically-qualified" representations of the elements of concern. 065 * 066 * For example, if "group" is a child of "groups", which is in turn a child of the root 067 * element "data", then it is represented as "data.groups.group" in the Map. 068 */ 069 public abstract List<KeyValue> getElementTransformationList(); 070 071 /** 072 * 073 * This method returns the element that we should start transforming at. 074 * So, if we had: 075 * <data> 076 * <groups> 077 * <group> 078 * 079 * We might want to start transforming at the group level. 080 * In that case the startingElement = "group" 081 * 082 * @return 083 */ 084 public abstract String getStartingElementPath(); 085 086 @Override 087 public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { 088 // Push the element onto the stack 089 if (groupXmlStack.isEmpty()){ 090 groupXmlStack.add(localName); 091 } 092 else { 093 // Push a child element by appending localName to the value of the top element in the stack 094 groupXmlStack.add(groupXmlStack.get(groupXmlStack.size()-1) + "." + localName); 095 } 096 097 // Fetch the current element from the top of the stack 098 String currentElementKey = groupXmlStack.get(groupXmlStack.size()-1); 099 CurrentElement currentElement = new CurrentElement(currentElementKey,uri, localName, qName, atts); 100 101 // Transform elements of concern: 102 if (getElementTransformationList().contains(new ConcreteKeyValue(getTrimmedCurrentElementKey(currentElementKey), uri))){ 103 CurrentElement transformedElement = this.transformStartElement(currentElement); 104 super.startElement(transformedElement.getUri(), transformedElement.getLocalName(), transformedElement.getqName(), transformedElement.getAttributes()); 105 } 106 else { 107 // Pass other elements through as they are 108 super.startElement(uri, localName, qName, atts); 109 } 110 } 111 112 protected String getTrimmedCurrentElementKey(String currentElementKey){ 113 return currentElementKey.replaceFirst(StringEscapeUtils.escapeJava(this.getStartingElementPath()+"."), ""); 114 } 115 @Override 116 public void endElement(String uri, String localName, String qName) throws SAXException { 117 // Fetch the current element from the top of the stack 118 String currentElementKey = groupXmlStack.get(groupXmlStack.size()-1); 119 CurrentElement currentElement = new CurrentElement(currentElementKey,uri, localName, qName); 120 121 if (getElementTransformationList().contains(new ConcreteKeyValue(getTrimmedCurrentElementKey(currentElementKey), uri))){ 122 CurrentElement transformedElement = this.transformEndElement(currentElement); 123 super.endElement(transformedElement.getUri(), transformedElement.getLocalName(), transformedElement.getqName()); 124 } 125 else { 126 // Pass other elements through as they are 127 super.endElement(uri, localName, qName); 128 } 129 130 // Pop the element from the stack if it's not empty 131 if (!groupXmlStack.isEmpty()){ 132 groupXmlStack.remove(currentElementKey); 133 } 134 } 135 136 public class CurrentElement { 137 String nameKey; 138 String uri; 139 String localName; 140 String qName; 141 Attributes attributes; 142 143 public CurrentElement(){} 144 145 public CurrentElement(String nameKey, String uri, String localName, 146 String qName) { 147 super(); 148 this.nameKey = nameKey; 149 this.uri = uri; 150 this.localName = localName; 151 this.qName = qName; 152 } 153 154 public CurrentElement(String nameKey,String uri, String localName, String qName, 155 Attributes attributes) { 156 super(); 157 this.nameKey = nameKey; 158 this.uri = uri; 159 this.localName = localName; 160 this.qName = qName; 161 this.attributes = attributes; 162 } 163 164 165 public String getUri() { 166 return this.uri; 167 } 168 public void setUri(String uri) { 169 this.uri = uri; 170 } 171 public String getLocalName() { 172 return this.localName; 173 } 174 public void setLocalName(String localName) { 175 this.localName = localName; 176 } 177 public String getqName() { 178 return this.qName; 179 } 180 public void setqName(String qName) { 181 this.qName = qName; 182 } 183 public Attributes getAttributes() { 184 return this.attributes; 185 } 186 public void setAttributes(Attributes attributes) { 187 this.attributes = attributes; 188 } 189 190 public String getNameKey() { 191 return this.nameKey; 192 } 193 194 public void setNameKey(String nameKey) { 195 this.nameKey = nameKey; 196 } 197 } 198 199}