001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.ajax;
018:
019: import java.io.IOException;
020: import java.util.Map;
021:
022: import org.apache.avalon.framework.parameters.Parameters;
023: import org.apache.cocoon.ProcessingException;
024: import org.apache.cocoon.environment.ObjectModelHelper;
025: import org.apache.cocoon.environment.Request;
026: import org.apache.cocoon.environment.SourceResolver;
027: import org.apache.cocoon.transformation.AbstractTransformer;
028: import org.apache.cocoon.xml.AttributesImpl;
029: import org.apache.cocoon.xml.RedundantNamespacesFilter;
030: import org.xml.sax.Attributes;
031: import org.xml.sax.Locator;
032: import org.xml.sax.SAXException;
033: import org.xml.sax.SAXParseException;
034:
035: /**
036: *
037: * @version $Id: BrowserUpdateTransformer.java 448479 2006-09-21 07:33:36Z crossley $
038: */
039:
040: public class BrowserUpdateTransformer extends AbstractTransformer {
041:
042: public static final String AJAXMODE_PARAM = "cocoon-ajax";
043: public static final String BU_NSURI = "http://apache.org/cocoon/browser-update/1.0";
044:
045: private boolean ajaxRequest = false;
046:
047: private int replaceDepth = 0;
048:
049: private boolean inUpdateTag = false;
050: private String updateTagId = null;
051:
052: Locator locator;
053:
054: public void setup(SourceResolver resolver, Map objectModel,
055: String src, Parameters par) throws ProcessingException,
056: SAXException, IOException {
057:
058: Request request = ObjectModelHelper.getRequest(objectModel);
059: this .ajaxRequest = request.getParameter(AJAXMODE_PARAM) != null;
060: this .replaceDepth = 0;
061: this .inUpdateTag = false;
062: }
063:
064: public void setDocumentLocator(Locator locator) {
065: super .setDocumentLocator(locator);
066: this .locator = locator;
067: }
068:
069: public void startDocument() throws SAXException {
070:
071: if (ajaxRequest) {
072: // Add the namespace filter to our own output.
073: // This is needed as flattening bu:* elements can lead to some weird reordering of
074: // namespace declarations...
075: RedundantNamespacesFilter nsPipe = new RedundantNamespacesFilter();
076: if (this .xmlConsumer != null) {
077: nsPipe.setConsumer(this .xmlConsumer);
078: } else {
079: nsPipe.setContentHandler(this .contentHandler);
080: }
081: setConsumer(nsPipe);
082: }
083:
084: super .startDocument();
085: if (ajaxRequest) {
086: // Add a root element. The original one is very likely to be stripped
087: super .startPrefixMapping("bu", BU_NSURI);
088: super .startElement(BU_NSURI, "document", "bu:document",
089: new AttributesImpl());
090: }
091: }
092:
093: public void startPrefixMapping(String prefix, String uri)
094: throws SAXException {
095: // Passthrough if not in ajax mode or if in a bu:replace
096: if (!this .ajaxRequest || this .replaceDepth > 0) {
097: super .startPrefixMapping(prefix, uri);
098: }
099: }
100:
101: public void startElement(String uri, String loc, String raw,
102: Attributes attrs) throws SAXException {
103: if (BU_NSURI.equals(uri) && "replace".equals(loc)) {
104: // Keep the id attribute. It may be null, in which case the one of the
105: // child element will be used.
106: this .updateTagId = attrs.getValue("id");
107: this .inUpdateTag = true;
108: if (this .ajaxRequest && this .replaceDepth == 0) {
109: // Pass this element through
110: super .startElement(uri, loc, raw, attrs);
111: }
112: replaceDepth++;
113: } else {
114: // Passthrough if not in ajax mode or if in a bu:replace
115:
116: // Is the enclosing element a bu:replace?
117: if (this .inUpdateTag) {
118: this .inUpdateTag = false;
119: // Is there already an id?
120: String localId = attrs.getValue("id");
121: if (localId != null) {
122: // Yes: check it's the same
123: if (this .updateTagId != null
124: && !localId.equals(this .updateTagId)) {
125: throw new SAXParseException(
126: "Id on bu:replace (" + this .updateTagId
127: + ") and " + raw + " ("
128: + localId + ") don't match.",
129: this .locator);
130: }
131: } else {
132: // No: add it
133: if (this .updateTagId == null) {
134: throw new SAXParseException(
135: "Neither bu:replace nor " + raw
136: + " have an id attribute.",
137: this .locator);
138: }
139: AttributesImpl newAttrs = new AttributesImpl(attrs);
140: newAttrs.addCDATAAttribute("id", this .updateTagId);
141: attrs = newAttrs;
142: }
143: this .updateTagId = null;
144: }
145: if (!this .ajaxRequest || this .replaceDepth > 0) {
146: super .startElement(uri, loc, raw, attrs);
147: }
148: }
149: }
150:
151: public void characters(char[] buffer, int offset, int len)
152: throws SAXException {
153: if (this .inUpdateTag) {
154: // Check that it's only spaces
155: for (int i = offset; i < len; i++) {
156: if (!Character.isWhitespace(buffer[i])) {
157: throw new SAXParseException(
158: "bu:replace must include a single child element and no text.",
159: this .locator);
160: }
161: }
162: }
163: if (!this .ajaxRequest || this .replaceDepth > 0) {
164: super .characters(buffer, offset, len);
165: }
166: }
167:
168: public void endElement(String uri, String loc, String raw)
169: throws SAXException {
170: if (BU_NSURI.equals(uri) && "replace".equals(loc)) {
171: replaceDepth--;
172: if (this .ajaxRequest && this .replaceDepth == 0) {
173: // Pass this element through
174: super .endElement(uri, loc, raw);
175: }
176: } else {
177: // Passthrough if not in ajax mode or if in a bu:replace
178: if (!this .ajaxRequest || this .replaceDepth > 0) {
179: super .endElement(uri, loc, raw);
180: }
181: }
182: }
183:
184: public void endPrefixMapping(String prefix) throws SAXException {
185: // Passthrough if not in ajax mode or if in a bu:replace
186: if (!this .ajaxRequest || this .replaceDepth > 0) {
187: super .endPrefixMapping(prefix);
188: }
189: }
190:
191: public void endDocument() throws SAXException {
192: if (ajaxRequest) {
193: super .endElement(BU_NSURI, "document", "bu:document");
194: super .endPrefixMapping("bu");
195: }
196: super .endDocument();
197: }
198:
199: public void recycle() {
200: this.locator = null;
201: this.updateTagId = null;
202: super.recycle();
203: }
204: }
|