001: // /////////////////////////////
002: // Makumba, Makumba tag library
003: // Copyright (C) 2000-2003 http://www.makumba.org
004: //
005: // This library is free software; you can redistribute it and/or
006: // modify it under the terms of the GNU Lesser General Public
007: // License as published by the Free Software Foundation; either
008: // version 2.1 of the License, or (at your option) any later version.
009: //
010: // This library is distributed in the hope that it will be useful,
011: // but WITHOUT ANY WARRANTY; without even the implied warranty of
012: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: // Lesser General Public License for more details.
014: //
015: // You should have received a copy of the GNU Lesser General Public
016: // License along with this library; if not, write to the Free Software
017: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: //
019: // -------------
020: // $Id: RecordEditor.java 1884 2007-10-21 23:35:41Z rosso_nero $
021: // $Name$
022: /////////////////////////////////////
023:
024: package org.makumba.forms.html;
025:
026: import java.util.ArrayList;
027: import java.util.Collection;
028: import java.util.Collections;
029: import java.util.Dictionary;
030: import java.util.Hashtable;
031: import java.util.Iterator;
032: import java.util.Vector;
033:
034: import javax.servlet.http.HttpServletRequest;
035:
036: import org.makumba.CompositeValidationException;
037: import org.makumba.DataDefinition;
038: import org.makumba.FieldDefinition;
039: import org.makumba.InvalidValueException;
040: import org.makumba.ValidationRule;
041: import org.makumba.commons.formatters.FieldFormatter;
042: import org.makumba.commons.formatters.RecordFormatter;
043: import org.makumba.forms.validation.ClientsideValidationProvider;
044: import org.makumba.providers.datadefinition.makumba.validation.ComparisonValidationRule;
045:
046: /**
047: * Editor of Makumba data. Each subclass knows how to format HTML <input> and <select> tags for each type of Makumba
048: * data, and how to read their data from HTTP query strings in form responses.
049: *
050: * @author Cristian Bogdan
051: * @author Rudolf Mayer
052: * @version $Id: RecordEditor.java 1884 2007-10-21 23:35:41Z rosso_nero $
053: */
054: public class RecordEditor extends RecordFormatter {
055: private static final long serialVersionUID = 1L;
056:
057: String database;
058:
059: String[] db;
060:
061: String[] query;
062:
063: protected RecordEditor() {
064: }
065:
066: public RecordEditor(DataDefinition ri, Hashtable h, String database) {
067: super (ri, h);
068: this .database = database;
069: db = new String[ri.getFieldNames().size()];
070: query = new String[ri.getFieldNames().size()];
071: }
072:
073: public ArrayList getUnassignedExceptions(
074: CompositeValidationException e,
075: ArrayList unassignedExceptions, String suffix) {
076: for (int i = 0; i < dd.getFieldNames().size(); i++) {
077: FieldEditor fe = (FieldEditor) formatterArray[i];
078: Collection exceptions = e.getExceptions(fe.getInputName(
079: this , i, suffix));
080: if (exceptions != null) {
081: for (Iterator iter = exceptions.iterator(); iter
082: .hasNext();) {
083: unassignedExceptions.remove(iter.next());
084: }
085: }
086: }
087: return unassignedExceptions;
088: }
089:
090: public void initClientSideValidation(
091: ClientsideValidationProvider provider,
092: boolean liveValidation, String suffix) {
093: for (int i = 0; i < dd.getFieldNames().size(); i++) {
094: FieldEditor fe = (FieldEditor) formatterArray[i];
095: FieldDefinition fieldDefinition = dd.getFieldDefinition(i);
096: String inputName = fe.getInputName(this , i, suffix);
097: if (inputName == null) {
098: continue;
099: }
100: provider.initField(inputName, fieldDefinition, /* validationDefinition, */
101: liveValidation);
102: }
103: }
104:
105: public Dictionary readFrom(HttpServletRequest req, String suffix,
106: boolean applyValidationRules) {
107: Dictionary<String, Object> data = new Hashtable<String, Object>();
108: Vector<Exception> exceptions = new Vector<Exception>(); // will collect all exceptions from the field validity checks
109:
110: Hashtable<Integer, Object> validatedFields = new Hashtable<Integer, Object>();
111: Hashtable<String, Object> validatedFieldsNameCache = new Hashtable<String, Object>();
112:
113: // we validate all fields in two passes - first we validate data type integrity, i.e. we let makumba check if
114: // the declared types in the MDD match with what we have in the form
115: for (int i = 0; i < dd.getFieldNames().size(); i++) {
116: FieldEditor fe = (FieldEditor) formatterArray[i];
117: String inputName = fe.getInputName(this , i, suffix);
118: if (inputName == null) {
119: continue;
120: }
121: Object o = null;
122: try {
123: o = fe
124: .readFrom(
125: this ,
126: i,
127: org.makumba.commons.attributes.RequestAttributes
128: .getParameters(req), suffix);
129: if (o != null) {
130: o = dd.getFieldDefinition(i).checkValue(o);
131: } else {
132: o = dd.getFieldDefinition(i).getNull();
133: }
134:
135: validatedFields.put(new Integer(i), o);
136: validatedFieldsNameCache.put(inputName, o);
137:
138: } catch (InvalidValueException e) {
139: // if there is an exception in this field
140: // we store it in the hash, together with the field definition where it occured
141: exceptions.add(e);
142: }
143: }
144:
145: ArrayList validatedFieldsOrdered = new ArrayList(
146: validatedFields.keySet());
147: Collections.sort(validatedFieldsOrdered);
148:
149: // in the second validation pass, we only validate those fields that passed the first check
150: // on those, we apply the user-defined checks from the validation definition
151: for (int index = 0; index < validatedFieldsOrdered.size(); index++) {
152: int i = ((Integer) validatedFieldsOrdered.get(index))
153: .intValue();
154: FieldEditor fe = (FieldEditor) formatterArray[i];
155: FieldDefinition fieldDefinition = dd.getFieldDefinition(i);
156: Object o = validatedFields.get(validatedFieldsOrdered
157: .get(index));
158: Collection validationRules = fieldDefinition
159: .getValidationRules();
160:
161: if (validationRules != null && applyValidationRules) {
162: for (Iterator iter = validationRules.iterator(); iter
163: .hasNext();) {
164: ValidationRule rule = (ValidationRule) iter.next();
165: try { // evaluate each rule separately
166: if (rule instanceof ComparisonValidationRule
167: && !((ComparisonValidationRule) rule)
168: .isCompareToExpression()) {
169: FieldDefinition otherFd = ((ComparisonValidationRule) rule)
170: .getOtherFd();
171: Object otherValue = validatedFieldsNameCache
172: .get(otherFd.getName());
173: if (otherValue != null) {
174: rule.validate(new Object[] { o,
175: otherValue });
176: }
177: } else {
178: rule.validate(o);
179: }
180: } catch (InvalidValueException e) {
181: exceptions.add(e);
182: }
183: }
184: }
185:
186: org.makumba.commons.attributes.RequestAttributes
187: .setAttribute(req, fe.getInputName(this , i, suffix)
188: + "_type", fieldDefinition);
189:
190: if (o != null) {
191: // the data is written in the dictionary without the suffix
192: data.put(fe.getInputName(this , i, ""), o);
193: }
194: org.makumba.commons.attributes.RequestAttributes
195: .setAttribute(req,
196: fe.getInputName(this , i, suffix), o);
197: }
198:
199: if (exceptions.size() > 0) {
200: throw new CompositeValidationException(exceptions);
201: }
202: return data;
203: }
204:
205: public void config() {
206: for (int i = 0; i < dd.getFieldNames().size(); i++) {
207: ((FieldEditor) formatterArray[i]).onStartup(this , i);
208: }
209: }
210:
211: public void initFormatters() {
212: formatterArray = new FieldFormatter[dd.getFieldNames().size()];
213: for (int i = 0; i < dd.getFieldNames().size(); i++) {
214: FieldDefinition fd = dd.getFieldDefinition(i);
215: switch (fd.getIntegerType()) {
216: case FieldDefinition._ptr:
217: formatterArray[i] = ptrEditor.getInstance();
218: break;
219: case FieldDefinition._ptrOne:
220: case FieldDefinition._setComplex:
221: formatterArray[i] = FieldEditor.getInstance();
222: break;
223: case FieldDefinition._int:
224: formatterArray[i] = intEditor.getInstance();
225: break;
226: case FieldDefinition._intEnum:
227: formatterArray[i] = intEnumEditor.getInstance();
228: break;
229: case FieldDefinition._char:
230: formatterArray[i] = charEditor.getInstance();
231: break;
232: case FieldDefinition._charEnum:
233: formatterArray[i] = charEnumEditor.getInstance();
234: break;
235: case FieldDefinition._text:
236: case FieldDefinition._binary:
237: formatterArray[i] = textEditor.getInstance();
238: break;
239: case FieldDefinition._date:
240: formatterArray[i] = dateEditor.getInstance();
241: break;
242: case FieldDefinition._set:
243: formatterArray[i] = setEditor.getInstance();
244: break;
245: // case FieldDefinition._nil:
246: // formatterArray[i] = nilEditor.getInstance();
247: // break;
248: case FieldDefinition._real:
249: formatterArray[i] = realEditor.getInstance();
250: break;
251: case FieldDefinition._setCharEnum:
252: formatterArray[i] = setcharEnumEditor.getInstance();
253: break;
254: case FieldDefinition._setIntEnum:
255: formatterArray[i] = setintEnumEditor.getInstance();
256: break;
257: case FieldDefinition._dateCreate:
258: case FieldDefinition._dateModify:
259: case FieldDefinition._ptrIndex:
260: case FieldDefinition._ptrRel:
261: formatterArray[i] = errorEditor.getInstance();
262: break;
263: default:
264: throw new RuntimeException(
265: "Internal Makumba error: Unknown FieldDefinition type lead to invalid formatter content. Please report to developers.");
266: }
267: }
268: }
269:
270: }
|