/**
  * Transform a particular attribute on a TagNode, iff that attribute exists
  *
  * @param context The ReplayParseContext being transformed
  * @param node the TagNode to update
  * @param attr the attribute name to transform
  * @param transformer the StringTransformer responsible for creating the new value
  * @return true if the attribute was found and updated
  */
 private boolean transformAttr(
     ReplayParseContext context, TagNode node, String attr, StringTransformer transformer) {
   String orig = node.getAttribute(attr);
   if (orig != null) {
     node.setAttribute(attr, transformer.transform(context, orig));
     return true;
   }
   return false;
 }
  /**
   * @param context
   * @param textNode
   * @throws IOException
   */
  private void handleJSTextNode(ReplayParseContext context, TextNode textNode) throws IOException {

    boolean alreadyInsertedHead = (context.getData(FERRET_HEAD_INSERTED) != null);

    context.incJSBlockCount();

    if (alreadyInsertedHead) {
      textNode.setText(jsBlockTrans.transform(context, textNode.getText()));
    }

    emit(context, null, textNode, null);
  }
  private void handleJSIncludeNode(ReplayParseContext context, TagNode tagNode) throws IOException {
    String file = tagNode.getAttribute("SRC");
    if (file != null) {
      // TODO: This is hacky.. fix it
      // This is used to check if the file should be skipped...
      // from a custom rule..
      String result = jsBlockTrans.transform(context, file);
      // The rewriting is done by the js_ rewriter
      if ((result != null) && !result.isEmpty()) {
        tagNode.setAttribute("SRC", jsUrlTrans.transform(context, file));
      } else {
        file = "";
        tagNode.setAttribute("SRC", jsUrlTrans.transform(context, file));
      }
    }

    emit(context, null, tagNode, null);
  }
  // TODO: This should all be refactored up into an abstract base class with
  // default no-op methods, allowing a subclass to only override the ones they
  // want...
  public void handleNode(ParseContext pContext, Node node) throws IOException {
    ReplayParseContext context = (ReplayParseContext) pContext;
    if (NodeUtils.isRemarkNode(node)) {
      RemarkNode remarkNode = (RemarkNode) node;
      remarkNode.setText(jsBlockTrans.transform(context, remarkNode.getText()));
      emit(context, null, node, null);

    } else if (NodeUtils.isTextNode(node)) {
      TextNode textNode = (TextNode) node;
      if (context.isInCSS()) {
        handleCSSTextNode(context, textNode);

      } else if (context.isInScriptText()) {
        handleJSTextNode(context, textNode);
      } else {
        emit(context, null, textNode, null);
        //				handleContentTextNode(context,textNode);
      }
    } else if (NodeUtils.isTagNode(node)) {
      TagNode tagNode = (TagNode) node;

      if (NodeUtils.isOpenTagNodeNamed(tagNode, NodeUtils.SCRIPT_TAG_NAME)) {
        handleJSIncludeNode(context, tagNode);
      } else if (tagNode.isEndTag()) {

        if (tagNode.getTagName().equals("HEAD")) {
          context.putData(FERRET_IN_HEAD, null);
        }

        if (checkAllowTag(pContext, tagNode)) {
          emit(context, null, tagNode, null);
        }

        //				handleCloseTagNode(context,tagNode);
      } else {
        // assume start, possibly empty:
        handleOpenTagNode(context, tagNode);
      }
    } else {
      throw new IllegalArgumentException("Unknown node type..");
    }
  }