001: /* ***** BEGIN LICENSE BLOCK *****
002: * Version: MPL 1.1
003: * The contents of this file are subject to the Mozilla Public License Version
004: * 1.1 (the "License"); you may not use this file except in compliance with
005: * the License. You may obtain a copy of the License at
006: * http://www.mozilla.org/MPL/
007: *
008: * Software distributed under the License is distributed on an "AS IS" basis,
009: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
010: * for the specific language governing rights and limitations under the
011: * License.
012: *
013: * The Original Code is Riot.
014: *
015: * The Initial Developer of the Original Code is
016: * Neteye GmbH.
017: * Portions created by the Initial Developer are Copyright (C) 2006
018: * the Initial Developer. All Rights Reserved.
019: *
020: * Contributor(s):
021: * Felix Gnass [fgnass at neteye dot de]
022: *
023: * ***** END LICENSE BLOCK ***** */
024: package org.riotfamily.forms.controller;
025:
026: import java.io.IOException;
027: import java.io.PrintWriter;
028: import java.io.StringWriter;
029: import java.util.HashSet;
030: import java.util.Iterator;
031: import java.util.LinkedHashSet;
032: import java.util.LinkedList;
033: import java.util.List;
034:
035: import javax.servlet.http.HttpServletResponse;
036:
037: import net.sf.json.JSONArray;
038:
039: import org.apache.commons.logging.Log;
040: import org.apache.commons.logging.LogFactory;
041: import org.riotfamily.common.web.util.ServletUtils;
042: import org.riotfamily.forms.DHTMLElement;
043: import org.riotfamily.forms.Element;
044: import org.riotfamily.forms.FormContext;
045: import org.riotfamily.forms.FormListener;
046: import org.riotfamily.forms.event.EventPropagation;
047: import org.riotfamily.forms.event.JavaScriptEventAdapter;
048: import org.riotfamily.forms.resource.FormResource;
049: import org.riotfamily.forms.resource.LoadingCodeGenerator;
050: import org.riotfamily.forms.resource.ResourceElement;
051:
052: /**
053: * FormListener implementation used by the
054: * {@link org.riotfamily.forms.controller.AjaxFormController AjaxFormController} to
055: * notify the client of structural changes. It creates a JSON response that
056: * contains the modifications to be performed on the client side DOM.
057: */
058: public class AjaxResponse implements FormListener {
059:
060: private Log log = LogFactory.getLog(AjaxResponse.class);
061:
062: private PrintWriter writer;
063:
064: private LinkedHashSet resources = new LinkedHashSet();
065:
066: private List propagations = new LinkedList();
067:
068: private List dhtmlElements = new LinkedList();
069:
070: private HashSet validatedElements = new HashSet();
071:
072: private Element focusedElement;
073:
074: private JSONArray json = new JSONArray();
075:
076: public AjaxResponse(HttpServletResponse response)
077: throws IOException {
078: response.setContentType("application/json;charset=UTF-8");
079: response.setCharacterEncoding("UTF-8");
080: ServletUtils.setNoCacheHeaders(response);
081: this .writer = response.getWriter();
082: }
083:
084: private String renderElement(Element element) {
085: StringWriter sw = new StringWriter();
086: PrintWriter pw = new PrintWriter(sw);
087: FormContext context = element.getForm().getFormContext();
088: context.setWriter(pw);
089: element.render(pw);
090: context.setWriter(writer);
091: return sw.toString();
092: }
093:
094: public void elementChanged(Element element) {
095: json.add(new Action("replace", element.getId(),
096: renderElement(element)));
097: validatedElements.add(element);
098: }
099:
100: public void elementValidated(Element element) {
101: validatedElements.add(element);
102: }
103:
104: public void elementRemoved(Element element) {
105: json.add(new Action("remove", element.getId()));
106: }
107:
108: public void elementAdded(Element element) {
109: json.add(new Action("insert", element.getParent().getId(),
110: renderElement(element)));
111: }
112:
113: public void elementFocused(Element element) {
114: log.debug("Focus requested for: " + element.getId());
115: focusedElement = element;
116: }
117:
118: public void elementEnabled(Element element) {
119: json.add(new Action("enable", element.getId(), String
120: .valueOf(element.isEnabled())));
121: }
122:
123: public void elementRendered(Element element) {
124: log.debug("Element rendered: " + element);
125: if (element instanceof JavaScriptEventAdapter) {
126: JavaScriptEventAdapter adapter = (JavaScriptEventAdapter) element;
127: EventPropagation.addPropagations(adapter, propagations);
128: }
129: if (element instanceof ResourceElement) {
130: ResourceElement re = (ResourceElement) element;
131: FormResource res = re.getResource();
132: if (res != null) {
133: resources.add(res);
134: }
135: }
136: if (element instanceof DHTMLElement) {
137: log.debug("DHTML element registered");
138: dhtmlElements.add(element);
139: }
140: }
141:
142: public void refresh(Element element) {
143: json.add(new Action("refresh", element.getId()));
144: }
145:
146: public void alert(String message) {
147: json.add(new Action("eval", null, "alert('" + message + "');"));
148: }
149:
150: private void renderPropagations() {
151: Iterator it = propagations.iterator();
152: while (it.hasNext()) {
153: EventPropagation p = (EventPropagation) it.next();
154: renderPropagation(p);
155: }
156: }
157:
158: private void renderPropagation(EventPropagation propagation) {
159: log.debug("Propagating " + propagation.getType()
160: + " events for element " + propagation.getId());
161:
162: json.add(new Action("propagate", propagation.getId(),
163: propagation.getType()));
164: }
165:
166: private void renderScripts() {
167: Iterator it = dhtmlElements.iterator();
168: while (it.hasNext()) {
169: DHTMLElement e = (DHTMLElement) it.next();
170: String script = e.getInitScript();
171: if (script != null) {
172: log.debug("Evaluating init script ...");
173: if (e instanceof ResourceElement) {
174: ResourceElement resEle = (ResourceElement) e;
175: FormResource res = resEle.getResource();
176: StringBuffer sb = new StringBuffer();
177: if (res != null) {
178: sb.append("Resources.execWhenLoaded(['");
179: sb.append(res.getUrl());
180: sb.append("'], function() {");
181: sb.append(script);
182: sb.append("})");
183: script = sb.toString();
184: }
185: }
186: json.add(new Action("eval", null, script));
187: }
188: }
189: }
190:
191: private void renderResources() {
192: StringWriter sw = new StringWriter();
193: PrintWriter pw = new PrintWriter(sw);
194: LoadingCodeGenerator.renderLoadingCode(resources, pw);
195: json.add(new Action("eval", null, sw.toString()));
196: }
197:
198: private void renderFocus() {
199: if (focusedElement != null) {
200: log.debug("Focusing element " + focusedElement.getId());
201: json.add(new Action("focus", focusedElement.getId()));
202: }
203: }
204:
205: private void renderErrors() {
206: Iterator it = validatedElements.iterator();
207: while (it.hasNext()) {
208: Element element = (Element) it.next();
209: if (element.getForm().getErrors().getErrors(element) != null) {
210: boolean valid = element.getForm().getErrors()
211: .hasErrors(element);
212: json.add(new Action("valid", element.getId(), String
213: .valueOf(valid)));
214:
215: StringWriter sw = new StringWriter();
216: PrintWriter pw = new PrintWriter(sw);
217: FormContext context = element.getForm()
218: .getFormContext();
219: context.setWriter(pw);
220: element.getForm().getErrors().renderErrors(element);
221: context.setWriter(writer);
222: json.add(new Action("error", element.getId(), sw
223: .toString()));
224: }
225: }
226: }
227:
228: private void onClose() {
229: renderResources();
230: renderFocus();
231: renderPropagations();
232: renderScripts();
233: renderErrors();
234: }
235:
236: public void close() {
237: onClose();
238: writer.print(json.toString());
239: }
240:
241: public static class Action {
242:
243: private String command;
244:
245: private String element;
246:
247: private String value;
248:
249: public Action(String command, String element) {
250: this (command, element, null);
251: }
252:
253: public Action(String command, String element, String value) {
254: this .command = command;
255: this .element = element;
256: this .value = value;
257: }
258:
259: public String getCommand() {
260: return this .command;
261: }
262:
263: public String getElement() {
264: return this .element;
265: }
266:
267: public String getValue() {
268: return this.value;
269: }
270:
271: }
272:
273: }
|