1 17 package org.apache.forrest.util; 18 19 import java.io.IOException ; 20 import java.io.Serializable ; 21 import java.net.URLEncoder ; 22 import java.util.Map ; 23 24 import org.apache.avalon.framework.activity.Disposable; 25 import org.apache.avalon.framework.configuration.Configurable; 26 import org.apache.avalon.framework.configuration.Configuration; 27 import org.apache.avalon.framework.configuration.ConfigurationException; 28 import org.apache.avalon.framework.parameters.Parameters; 29 import org.apache.avalon.framework.service.ServiceManager; 30 import org.apache.avalon.framework.service.ServiceException; 31 import org.apache.cocoon.transformation.AbstractDOMTransformer; 32 import org.apache.cocoon.ProcessingException; 33 import org.apache.cocoon.caching.CacheableProcessingComponent; 34 import org.apache.cocoon.environment.SourceResolver; 35 import org.apache.cocoon.util.HashUtil; 36 import org.apache.excalibur.source.SourceValidity; 37 import org.apache.excalibur.source.impl.validity.NOPValidity; 38 import org.apache.excalibur.xml.xpath.XPathProcessor; 39 import org.w3c.dom.Document ; 40 import org.w3c.dom.Element ; 41 import org.w3c.dom.NodeList ; 42 import org.xml.sax.SAXException ; 43 44 81 public class IdGeneratorTransformer 82 extends AbstractDOMTransformer 83 implements CacheableProcessingComponent, Configurable, Disposable 84 { 85 86 87 private XPathProcessor processor = null; 88 89 protected String elementXPath = null; 90 protected String idXPath = null; 91 protected String idAttr = null; 92 93 public void configure(Configuration configuration) throws ConfigurationException { 94 getLogger().info("## || Configuring IdGeneratorTransformer with "+configuration); 95 this.elementXPath = configuration.getChild("element").getValue(null); 96 this.idXPath = configuration.getChild("id").getValue(null); 97 this.idAttr = configuration.getChild("id-attr").getValue("id"); 98 if (elementXPath == null) { 99 throw new ConfigurationException( 100 "## The IdGenerator 'element' parameter must be specified. For example, "+ 101 "<element>/document/body//*[local-name() = 'section']</element>"); 102 } 103 if (idXPath == null) { 104 throw new ConfigurationException( 105 "## The IdGenerator 'id' parameter must be specified. For example,"+ 106 "<id>title/text()</id>"); 107 } 108 } 109 110 public void setup(SourceResolver resolver, Map objectModel, 111 String source, Parameters parameters) 112 throws ProcessingException, SAXException , IOException 113 { 114 super.setup(resolver, objectModel, source, parameters); 115 133 } 134 135 public void service(ServiceManager manager) throws ServiceException { 136 super.service(manager); 137 try { 138 this.processor = (XPathProcessor)this.manager.lookup(XPathProcessor.ROLE); 139 } catch (Exception e) { 140 getLogger().error("cannot obtain XPathProcessor", e); 141 } 142 } 143 144 148 protected Document transform(Document doc) { 149 getLogger().debug("## Transforming with element='"+elementXPath+"', id='"+idXPath+"'"); 150 Document newDoc = null; 151 try { 152 newDoc = addIds(doc, elementXPath, idXPath); 153 } catch (SAXException se) { 154 getLogger().error("Error when transforming XML: "+se.getMessage(), se.getException()); 156 throw new RuntimeException ("Error transforming XML. See error log for details: "+se.getMessage()+". Nested exception: "+se.getException().getMessage()); 157 } 158 return newDoc; 159 } 160 161 private Document addIds(Document doc, String elementXPath, String idXPath) throws SAXException { 162 getLogger().debug("## Using element XPath "+elementXPath); 163 NodeList sects = processor.selectNodeList(doc, elementXPath); 164 getLogger().debug("## .. got "+sects.getLength()+" sections"); 165 for (int i=0; i<sects.getLength(); i++) { 166 Element sect = (Element )sects.item(i); 167 if (!sect.hasAttribute(this.idAttr)) { 168 sect.normalize(); 169 getLogger().debug("## Using id XPath "+idXPath); 170 String id = null; 171 try { 172 id = processor.evaluateAsString(sect, idXPath).trim(); 173 } catch (Exception e) { 174 throw new SAXException ("'id' XPath expression '"+idXPath+"' does not return a text node: "+e, e); 175 } 176 getLogger().info("## Got id "+id); 177 String newId; 184 try { 185 newId = URLEncoder.encode(id, "UTF-8"); 186 } 187 catch( java.io.UnsupportedEncodingException e ) 188 { 189 getLogger().error("cannot encode Id, using generate-id instead...", e); 190 newId = processor.evaluateAsString(sect, "generate-id()"); 191 } 192 newId = avoidConflicts(doc, sect, this.idAttr, newId); 193 sect.setAttributeNS(sect.getNamespaceURI(), this.idAttr, newId); 196 } 197 } 198 return doc; 199 } 200 201 206 private String avoidConflicts(Document doc, Element sect, String idAttr, String newId) { 207 NodeList conflicts = processor.selectNodeList(doc, "//*[@"+idAttr+"='"+newId+"']"); 209 int numConflicts = conflicts.getLength(); 210 getLogger().info("## "+numConflicts+" conflicts with "+newId); 211 if (numConflicts != 0) { 212 newId += "-"+processor.evaluateAsString(sect, "generate-id()"); 213 } 214 return newId; 215 } 216 217 219 226 public Serializable getKey() { 227 return ""+HashUtil.hash(this.elementXPath+this.idXPath); 228 } 229 230 public Serializable generateKey() { 232 return getKey(); 233 } 234 235 236 242 public SourceValidity getValidity() { 243 return new NOPValidity(); 244 } 245 246 public SourceValidity generateValidity() { 247 return getValidity(); 248 } 249 250 251 254 public void recycle() { 255 super.recycle(); 256 } 261 262 265 public void dispose() { 266 super.dispose(); 267 this.processor = null; 268 this.elementXPath = null; 269 this.idXPath = null; 270 this.idAttr = null; 271 } 272 } 273 | Popular Tags |