1 16 package org.apache.cocoon.transformation; 17 18 import java.io.IOException ; 19 import java.util.HashMap ; 20 import java.util.HashSet ; 21 import java.util.Map ; 22 import java.util.Set ; 23 import java.util.StringTokenizer ; 24 25 import org.apache.avalon.framework.activity.Disposable; 26 import org.apache.avalon.framework.activity.Initializable; 27 import org.apache.avalon.framework.configuration.Configuration; 28 import org.apache.avalon.framework.configuration.ConfigurationException; 29 import org.apache.avalon.framework.parameters.ParameterException; 30 import org.apache.avalon.framework.parameters.Parameters; 31 import org.apache.cocoon.ProcessingException; 32 import org.apache.cocoon.components.modules.input.InputModuleHelper; 33 import org.apache.cocoon.environment.SourceResolver; 34 import org.apache.cocoon.transformation.helpers.VariableConfiguration; 35 import org.apache.regexp.RE; 36 import org.apache.regexp.RECompiler; 37 import org.apache.regexp.REProgram; 38 import org.apache.regexp.RESyntaxException; 39 import org.xml.sax.Attributes ; 40 import org.xml.sax.SAXException ; 41 import org.xml.sax.helpers.AttributesImpl ; 42 43 221 public class LinkRewriterTransformer extends AbstractSAXTransformer 222 implements Initializable, Disposable { 223 224 private final static String NAMESPACE = ""; 225 226 230 private final static Object NO_REGEXP = new Object (); 231 232 236 237 private Configuration origConf; 238 239 private String origBadLinkStr; 240 private String origInSchemes; 241 private String origOutSchemes; 242 private String origNamespaceURI; 243 244 253 private Map origLinkAttrs; 254 255 259 263 private Configuration conf; 264 265 269 private String badLinkStr; 270 271 272 private Set inSchemes; 273 274 275 private Set outSchemes; 276 277 282 private Map linkAttrs; 283 284 private InputModuleHelper modHelper; 285 286 287 291 public void configure(Configuration conf) throws ConfigurationException { 292 super.configure(conf); 293 294 this.origConf = conf; 295 this.origBadLinkStr = conf.getChild("bad-link-str").getValue(null); 296 this.origInSchemes = conf.getChild("schemes").getValue(""); 297 this.origOutSchemes = conf.getChild("exclude-schemes").getValue("http https ftp news mailto"); 298 299 this.origNamespaceURI = conf.getChild("namespace-uri").getValue(NAMESPACE); 300 301 307 308 String linkAttrsValue = conf.getChild("link-attrs").getValue(""); 309 this.origLinkAttrs = split(linkAttrsValue, " ", NO_REGEXP); 310 311 Configuration[] attrConfs = conf.getChildren("link-attr"); 312 if (attrConfs.length > 0) { 313 RECompiler compiler = new RECompiler(); 314 for (int i = 0; i < attrConfs.length; i++) { 315 String attr = attrConfs[i].getAttribute("name"); 316 if (getLogger().isWarnEnabled() && origLinkAttrs.containsKey(attr)) { 317 getLogger().warn("Duplicate configuration entry found for attribute '" + 318 attr + "', overwriting previous configuration"); 319 } 320 321 String pattern = attrConfs[i].getAttribute("pattern", null); 322 if (pattern == null) { 323 this.origLinkAttrs.put(attr, NO_REGEXP); 324 } else { 325 try { 326 this.origLinkAttrs.put(attr, compiler.compile(pattern)); 327 } catch (RESyntaxException e) { 328 String msg = "Invalid regexp pattern '" + pattern + "' specified for attribute '" + attr + "'"; 329 throw new ConfigurationException(msg, attrConfs[i], e); 330 } 331 } 332 } 333 } 334 335 if (this.origLinkAttrs.size() == 0) { 337 this.origLinkAttrs.put("href", NO_REGEXP); 338 } 339 } 340 341 344 public void initialize() throws Exception { 345 this.modHelper = new InputModuleHelper(); 346 this.modHelper.setup(this.manager); 347 } 348 349 352 public void setup(SourceResolver resolver, 353 Map objectModel, 354 String src, 355 Parameters parameters) 356 throws ProcessingException, SAXException , IOException { 357 super.setup(resolver, objectModel, src, parameters); 358 359 this.badLinkStr = parameters.getParameter("bad-link-str", this.origBadLinkStr); 362 this.namespaceURI = parameters.getParameter("namespace-uri", this.origNamespaceURI); 363 364 this.inSchemes = split(parameters.getParameter("schemes", this.origInSchemes), " "); 365 this.outSchemes = split(parameters.getParameter("exclude-schemes", this.origOutSchemes), " "); 366 367 this.linkAttrs = this.origLinkAttrs; 368 if (parameters.isParameter("link-attrs")) { 369 try { 370 this.linkAttrs = split(parameters.getParameter("link-attrs"), " ", NO_REGEXP); 371 } catch (ParameterException ex) { 372 } 374 } 375 376 if (getLogger().isDebugEnabled()) { 377 getLogger().debug("bad-link-str = " + badLinkStr); 378 getLogger().debug("link-attrs = " + linkAttrs); 379 getLogger().debug("schemes = " + inSchemes); 380 getLogger().debug("exclude-schemes = " + outSchemes); 381 getLogger().debug("namespace-uri = " + namespaceURI); 382 } 383 384 VariableConfiguration varConf = new VariableConfiguration(this.origConf); 386 varConf.addVariable("src", src); 387 varConf.addVariables(parameters); 388 try { 389 this.conf = varConf.getConfiguration(); 390 } catch (ConfigurationException ce) { 391 throw new ProcessingException("Couldn't create dynamic config ", ce); 392 } 393 } 394 395 396 public void recycle() { 397 this.conf = null; 400 this.badLinkStr = null; 401 this.linkAttrs = null; 402 this.inSchemes = null; 403 this.outSchemes = null; 404 405 super.recycle(); 406 } 407 408 415 private Set split(String str, String delim) { 416 if (str == null) { 417 return null; 418 } 419 420 Set tokens = new HashSet (); 421 StringTokenizer st = new StringTokenizer (str, delim); 422 while (st.hasMoreTokens()) { 423 tokens.add(st.nextToken()); 424 } 425 return tokens; 426 } 427 428 436 private Map split(String str, String delim, Object valueObj) { 437 if (str == null) { 438 return null; 439 } 440 441 Map schemes = new HashMap (); 443 StringTokenizer st = new StringTokenizer (str, delim); 444 while (st.hasMoreTokens()) { 445 String pfx = st.nextToken(); 446 if (schemes.containsKey(pfx) && getLogger().isWarnEnabled()) { 447 getLogger().warn("Duplicate configuration entry found for attribute '" + 448 pfx + "', overwriting previous configuration"); 449 } 450 schemes.put(pfx, valueObj); 451 } 452 return schemes; 453 } 454 455 463 public void startTransformingElement(String uri, 464 String name, 465 String raw, 466 Attributes attr) 467 throws ProcessingException, IOException , SAXException { 468 boolean matched = false; 469 470 for (int attrIdx = 0; attrIdx < attr.getLength(); attrIdx++) { 471 String attrName = attr.getQName(attrIdx); 472 473 String attrValue = createTransformedAttr(attrName, attr.getValue(attrIdx)); 474 if (attrValue != null) { 475 if (!matched) { 476 attr = new AttributesImpl (attr); 477 matched = true; 478 } 479 ((AttributesImpl ) attr).setValue(attrIdx, attrValue); 480 } 481 } 482 super.startTransformingElement(uri, name, raw, attr); 483 } 484 485 494 private String createTransformedAttr( 495 String attrName, 496 String oldAttrValue) { 497 if (!linkAttrs.containsKey(attrName)) { 498 return null; 499 } 500 501 String newAttrValue = null; 502 Object reProgram = linkAttrs.get(attrName); 503 if (reProgram == NO_REGEXP) { 504 newAttrValue = createTransformedLink(oldAttrValue); 505 } else { 506 RE r = new RE((REProgram) reProgram); 508 if (r.match(oldAttrValue)) { 509 StringBuffer bufOut = new StringBuffer (oldAttrValue); 510 int offset = 0; 511 String link = null; 512 String newLink = null; 513 boolean modified = false; 514 515 for (int i = 1; i < r.getParenCount(); i++) { 517 link = r.getParen(i); 518 newLink = createTransformedLink(link); 519 if (newLink != null) { 520 bufOut.replace(r.getParenStart(i) + offset, 521 r.getParenEnd(i) + offset, 522 newLink); 523 offset += newLink.length() - r.getParenLength(i); 524 modified = true; 525 } 526 } 527 if (modified) { 528 newAttrValue = bufOut.toString(); 529 } 530 } 531 } 532 533 return newAttrValue; 534 } 535 536 543 private String createTransformedLink(String oldLink) { 544 String newLink = null; 545 int i = oldLink.indexOf(":"); 546 if (i != -1) { 547 String scheme = oldLink.substring(0, i); 548 String addr = oldLink.substring(i + 1); 549 if (outSchemes.contains(scheme)) { 550 if (getLogger().isDebugEnabled()) { 551 getLogger().debug("Ignoring link '" + oldLink + "'"); 552 } 553 } else if (inSchemes.contains(scheme) || inSchemes.size() == 0) { 554 try { 557 newLink = (String ) modHelper.getAttribute(this.objectModel, 558 getConf(scheme), 559 scheme, 560 addr, 561 (badLinkStr != null? badLinkStr: scheme + ":" + addr)); 562 if (getLogger().isDebugEnabled()) { 563 getLogger().debug("Converted link '" + oldLink + "' to '" + newLink + "'"); 564 } 565 } catch (org.apache.avalon.framework.CascadingRuntimeException e) { 566 if (e.getCause() instanceof ConfigurationException) { 568 throw e; 569 } 570 571 if (getLogger().isErrorEnabled()) { 575 getLogger().error("Error rewriting link '" + oldLink + "': " + 576 e.getMessage()); 577 } 578 } 579 } 580 } 581 return newLink; 582 } 583 584 590 private Configuration getConf(String scheme) { 591 Configuration[] schemeConfs = this.conf.getChildren("input-module"); 592 for (int i = 0; i < schemeConfs.length; i++) { 593 if (scheme.equals(schemeConfs[i].getAttribute("name", null))) { 594 return schemeConfs[i]; 595 } 596 } 597 return null; 598 } 599 600 603 public void dispose() { 604 if (this.modHelper != null) { 605 this.modHelper.releaseAll(); 606 this.modHelper = null; 607 } 608 super.dispose(); 609 } 610 } 611 | Popular Tags |