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: * $Header:$
018: */
019: package org.apache.beehive.netui.compiler.genmodel;
020:
021: import org.apache.beehive.netui.compiler.CompilerUtils;
022: import org.apache.beehive.netui.compiler.FatalCompileTimeException;
023: import org.apache.beehive.netui.compiler.JpfLanguageConstants;
024: import org.apache.beehive.netui.compiler.MergedControllerAnnotation;
025: import org.apache.beehive.netui.compiler.model.FormBeanModel;
026: import org.apache.beehive.netui.compiler.model.XmlModelWriterException;
027: import org.apache.beehive.netui.compiler.model.validation.ValidationModel;
028: import org.apache.beehive.netui.compiler.model.validation.ValidatorConstants;
029: import org.apache.beehive.netui.compiler.model.validation.ValidatorRule;
030: import org.apache.beehive.netui.compiler.typesystem.declaration.AnnotationInstance;
031: import org.apache.beehive.netui.compiler.typesystem.declaration.AnnotationValue;
032: import org.apache.beehive.netui.compiler.typesystem.declaration.ClassDeclaration;
033: import org.apache.beehive.netui.compiler.typesystem.declaration.MethodDeclaration;
034: import org.apache.beehive.netui.compiler.typesystem.declaration.Modifier;
035: import org.apache.beehive.netui.compiler.typesystem.declaration.ParameterDeclaration;
036: import org.apache.beehive.netui.compiler.typesystem.declaration.TypeDeclaration;
037: import org.apache.beehive.netui.compiler.typesystem.env.CoreAnnotationProcessorEnv;
038: import org.apache.beehive.netui.compiler.typesystem.type.DeclaredType;
039: import org.apache.beehive.netui.compiler.typesystem.type.ClassType;
040: import org.apache.beehive.netui.compiler.typesystem.type.TypeInstance;
041:
042: import java.io.File;
043: import java.io.FileNotFoundException;
044: import java.io.IOException;
045: import java.io.PrintWriter;
046: import java.util.ArrayList;
047: import java.util.Collection;
048: import java.util.HashSet;
049: import java.util.Iterator;
050: import java.util.List;
051: import java.util.Locale;
052: import java.util.Map;
053:
054: public class GenValidationModel extends ValidationModel implements
055: JpfLanguageConstants, ValidatorConstants {
056: private static final String STRUTS_VALIDATION_PREFIX = "validation";
057: private static final ValidatorRuleFactory VALIDATOR_RULE_FACTORY = new DefaultValidatorRuleFactory();
058:
059: private GenStrutsApp _strutsApp;
060: private File _mergeFile;
061: private CoreAnnotationProcessorEnv _env;
062: private HashSet processedFormBeanClasses = new HashSet();
063:
064: public GenValidationModel(ClassDeclaration jclass,
065: GenStrutsApp strutsApp, CoreAnnotationProcessorEnv env)
066: throws FatalCompileTimeException {
067: MergedControllerAnnotation mca = strutsApp
068: .getFlowControllerInfo()
069: .getMergedControllerAnnotation();
070: _strutsApp = strutsApp;
071: setValidatorVersion(mca.getValidatorVersion());
072: addRulesFromBeans(jclass);
073: addRulesFromActions(jclass, mca);
074: addRulesFromClass(mca);
075: String mergeFileName = mca.getValidatorMerge();
076: _mergeFile = strutsApp.getMergeFile(mergeFileName);
077: _env = env;
078: }
079:
080: private void addRulesFromBeans(ClassDeclaration jclass) {
081: //
082: // Read validation rules from public static inner classes (beans).
083: //
084: Collection innerTypes = CompilerUtils
085: .getClassNestedTypes(jclass);
086:
087: for (Iterator ii = innerTypes.iterator(); ii.hasNext();) {
088: TypeDeclaration innerType = (TypeDeclaration) ii.next();
089: if (innerType instanceof ClassDeclaration
090: && innerType.hasModifier(Modifier.PUBLIC)
091: && innerType.hasModifier(Modifier.STATIC)) {
092: addRulesFromBeanClass((ClassDeclaration) innerType);
093: }
094: }
095: }
096:
097: private void addRulesFromBeanClass(ClassDeclaration beanClass) {
098: String className = beanClass.getQualifiedName();
099: if (processedFormBeanClasses.contains(className)) {
100: return;
101: } else {
102: processedFormBeanClasses.add(className);
103: }
104:
105: Collection properties = CompilerUtils.getBeanProperties(
106: beanClass, true);
107:
108: List formNames = null;
109: if (properties != null && !properties.isEmpty()) {
110: formNames = getFormBeanNames(beanClass);
111: }
112:
113: for (Iterator ii = properties.iterator(); ii.hasNext();) {
114: CompilerUtils.BeanPropertyDeclaration property = (CompilerUtils.BeanPropertyDeclaration) ii
115: .next();
116: MethodDeclaration getter = property.getGetter();
117: String propertyName = property.getPropertyName();
118:
119: if (getter != null) {
120: //
121: // Parse validation annotations on each getter.
122: //
123: AnnotationInstance[] annotations = getter
124: .getAnnotationInstances();
125:
126: if (annotations != null) {
127: for (Iterator j = formNames.iterator(); j.hasNext();) {
128: String formName = (String) j.next();
129:
130: for (int i = 0; i < annotations.length; i++) {
131: AnnotationInstance ann = annotations[i];
132:
133: if (CompilerUtils.isJpfAnnotation(ann,
134: VALIDATABLE_PROPERTY_TAG_NAME)) {
135: // Add field rules from the Jpf.ValidationLocaleRules annotation.
136: addRulesFromAnnotation(ann, formName,
137: propertyName);
138: }
139: }
140: }
141: }
142: }
143: }
144: }
145:
146: private void addRulesFromAnnotation(
147: AnnotationInstance validationFieldAnn, String entityName,
148: String propertyName) {
149: //
150: // Add rules from the FieldValidationRules annotations in the "localeRules" member.
151: //
152: Collection localeRulesAnnotations = CompilerUtils
153: .getAnnotationArray(validationFieldAnn,
154: LOCALE_RULES_ATTR, false);
155: String displayName = CompilerUtils.getString(
156: validationFieldAnn, DISPLAY_NAME_ATTR, true);
157: String displayNameKey = CompilerUtils.getString(
158: validationFieldAnn, DISPLAY_NAME_KEY_ATTR, true);
159: RuleInfo ruleInfo = new RuleInfo(entityName, propertyName,
160: displayName, displayNameKey);
161:
162: for (Iterator ii = localeRulesAnnotations.iterator(); ii
163: .hasNext();) {
164: AnnotationInstance ann = (AnnotationInstance) ii.next();
165: addFieldRules(ann, ruleInfo, false);
166: }
167:
168: addFieldRules(validationFieldAnn, ruleInfo, true);
169: }
170:
171: private void addRulesFromActions(ClassDeclaration jclass,
172: MergedControllerAnnotation mca) {
173: MethodDeclaration[] methods = CompilerUtils.getClassMethods(
174: jclass, ACTION_TAG_NAME);
175:
176: for (int i = 0; i < methods.length; i++) {
177: MethodDeclaration method = methods[i];
178: AnnotationInstance actionAnnotation = CompilerUtils
179: .getAnnotation(method, ACTION_TAG_NAME);
180: assert actionAnnotation != null;
181: addRulesFromActionAnnotation(actionAnnotation, method
182: .getSimpleName());
183:
184: ParameterDeclaration[] parameters = method.getParameters();
185: if (parameters.length > 0) {
186: TypeInstance type = parameters[0].getType();
187:
188: if (type instanceof ClassType) {
189: ClassDeclaration classDecl = ((ClassType) type)
190: .getClassTypeDeclaration();
191: if (classDecl.getDeclaringType() == null)
192: addRulesFromBeanClass(classDecl);
193: }
194: }
195: }
196:
197: Collection simpleActions = mca.getSimpleActions();
198:
199: if (simpleActions != null) {
200: for (Iterator ii = simpleActions.iterator(); ii.hasNext();) {
201: AnnotationInstance simpleAction = (AnnotationInstance) ii
202: .next();
203: String actionName = CompilerUtils.getString(
204: simpleAction, NAME_ATTR, true);
205: assert actionName != null; //checker should enforce this.
206: addRulesFromActionAnnotation(simpleAction, actionName);
207: }
208: }
209: }
210:
211: private void addRulesFromActionAnnotation(
212: AnnotationInstance actionAnnotation, String actionName) {
213: Collection validatablePropertyAnnotations = CompilerUtils
214: .getAnnotationArray(actionAnnotation,
215: VALIDATABLE_PROPERTIES_ATTR, false);
216:
217: for (Iterator ii = validatablePropertyAnnotations.iterator(); ii
218: .hasNext();) {
219: AnnotationInstance validationFieldAnnotation = (AnnotationInstance) ii
220: .next();
221: String propertyName = CompilerUtils
222: .getString(validationFieldAnnotation,
223: PROPERTY_NAME_ATTR, true);
224: assert propertyName != null; // TODO: checker must enforce this
225: assert !propertyName.equals(""); // TODO: checker must enforce this
226:
227: //
228: // Add the rules, and associate them with the action path ("/" + the action name).
229: //
230: String actionPath = '/' + actionName; // Struts validator needs the slash in front
231: addRulesFromAnnotation(validationFieldAnnotation,
232: actionPath, propertyName);
233: }
234: }
235:
236: /**
237: * Returns a list of String names.
238: */
239: private List getFormBeanNames(TypeDeclaration beanType) {
240: List formBeans = _strutsApp
241: .getMatchingFormBeans(beanType, null);
242: List formBeanNames = new ArrayList();
243:
244: for (Iterator i = formBeans.iterator(); i.hasNext();) {
245: FormBeanModel formBeanModel = (FormBeanModel) i.next();
246: formBeanNames.add(formBeanModel.getName());
247: }
248:
249: return formBeanNames;
250: }
251:
252: private void addRulesFromClass(MergedControllerAnnotation mca) {
253: Collection validationBeanAnnotations = mca
254: .getValidatableBeans();
255:
256: for (Iterator ii = validationBeanAnnotations.iterator(); ii
257: .hasNext();) {
258: AnnotationInstance validationBeanAnnotation = (AnnotationInstance) ii
259: .next();
260: DeclaredType beanType = CompilerUtils.getDeclaredType(
261: validationBeanAnnotation, TYPE_ATTR, true);
262: assert beanType != null; // checker should enforce this
263: List formNames = getFormBeanNames(CompilerUtils
264: .getDeclaration(beanType));
265: Collection validationFieldAnnotations = CompilerUtils
266: .getAnnotationArray(validationBeanAnnotation,
267: VALIDATABLE_PROPERTIES_ATTR, false);
268:
269: for (Iterator i2 = validationFieldAnnotations.iterator(); i2
270: .hasNext();) {
271: AnnotationInstance validationFieldAnnotation = (AnnotationInstance) i2
272: .next();
273: String propName = CompilerUtils.getString(
274: validationFieldAnnotation, PROPERTY_NAME_ATTR,
275: true);
276: assert propName != null; // checker should enforce this
277: assert !propName.equals(""); // TODO: get checker to enforce this
278:
279: //
280: // Add the rules. If the bean is derived from ActionForm, associate the rules with the *name* of
281: // the form; otherwise, associate them with the classname of the bean type.
282: //
283: for (Iterator j = formNames.iterator(); j.hasNext();) {
284: String formName = (String) j.next();
285: addRulesFromAnnotation(validationFieldAnnotation,
286: formName, propName);
287: }
288: }
289: }
290: }
291:
292: /**
293: * Add field rules from either a Jpf.ValidationField or a Jpf.ValidationLocaleRules annotation.
294: */
295: private void addFieldRules(
296: AnnotationInstance rulesContainerAnnotation,
297: RuleInfo ruleInfo, boolean applyToAllLocales) {
298: //
299: // First parse the locale from the wrapper annotation. This will apply to all rules inside.
300: //
301: Locale locale = null;
302:
303: if (!applyToAllLocales) {
304: String language = CompilerUtils.getString(
305: rulesContainerAnnotation, LANGUAGE_ATTR, true);
306:
307: //
308: // If there's no language specified, then this rule will only apply for the default ruleset
309: // (i.e., if there are explicit rules for the requested locale, this rule will not be run).
310: //
311: if (language != null) {
312: String country = CompilerUtils.getString(
313: rulesContainerAnnotation, COUNTRY_ATTR, true);
314: String variant = CompilerUtils.getString(
315: rulesContainerAnnotation, VARIANT_ATTR, true);
316:
317: language = language.trim();
318: if (country != null)
319: country = country.trim();
320: if (variant != null)
321: variant = variant.trim();
322:
323: if (country != null && variant != null)
324: locale = new Locale(language, country, variant);
325: else if (country != null)
326: locale = new Locale(language, country);
327: else
328: locale = new Locale(language);
329: }
330: }
331:
332: Map valuesPresent = rulesContainerAnnotation.getElementValues();
333:
334: for (Iterator ii = valuesPresent.entrySet().iterator(); ii
335: .hasNext();) {
336: Map.Entry entry = (Map.Entry) ii.next();
337: AnnotationValue value = (AnnotationValue) entry.getValue();
338: Object val = value.getValue();
339:
340: if (val instanceof AnnotationInstance) {
341: addFieldRuleFromAnnotation(ruleInfo,
342: (AnnotationInstance) val, locale,
343: applyToAllLocales);
344: } else if (val instanceof List) {
345: List annotations = CompilerUtils
346: .getAnnotationArray(value);
347:
348: for (Iterator i3 = annotations.iterator(); i3.hasNext();) {
349: AnnotationInstance i = (AnnotationInstance) i3
350: .next();
351: addFieldRuleFromAnnotation(ruleInfo, i, locale,
352: applyToAllLocales);
353: }
354: }
355: }
356:
357: setEmpty(false); // this ValidationModel is only "empty" if there are no rules.
358: }
359:
360: private void addFieldRuleFromAnnotation(RuleInfo ruleInfo,
361: AnnotationInstance annotation, Locale locale,
362: boolean applyToAllLocales) {
363:
364: ValidatorRule rule = getFieldRule(ruleInfo.getEntityName(),
365: ruleInfo.getFieldName(), annotation);
366:
367: if (rule != null) {
368: if (applyToAllLocales) {
369: addFieldRuleForAllLocales(ruleInfo, rule);
370: } else {
371: addFieldRule(ruleInfo, rule, locale);
372: }
373: }
374: }
375:
376: private static ValidatorRule getFieldRule(String entityName,
377: String propertyName, AnnotationInstance ruleAnnotation) {
378: ValidatorRule rule = VALIDATOR_RULE_FACTORY.getFieldRule(
379: entityName, propertyName, ruleAnnotation);
380:
381: if (rule != null) {
382: //
383: // message/message-key
384: //
385: rule.setMessage(CompilerUtils.getString(ruleAnnotation,
386: MESSAGE_ATTR, true));
387: rule.setMessageKey(CompilerUtils.getString(ruleAnnotation,
388: MESSAGE_KEY_ATTR, true));
389: rule.setBundle(CompilerUtils.getString(ruleAnnotation,
390: BUNDLE_NAME_ATTR, true));
391: if (rule.getMessage() != null)
392: assert rule.getMessageKey() == null; // TODO: checker should enforce
393:
394: //
395: // args
396: //
397: addMessageArgs(rule, ruleAnnotation);
398: }
399:
400: return rule;
401: }
402:
403: protected static void addMessageArgs(ValidatorRule rule,
404: AnnotationInstance annotation) {
405: List messageArgs = CompilerUtils.getAnnotationArray(annotation,
406: MESSAGE_ARGS_ATTR, true);
407:
408: if (messageArgs != null) {
409: int inferredPosition = 0;
410: for (Iterator ii = messageArgs.iterator(); ii.hasNext();) {
411: AnnotationInstance ann = (AnnotationInstance) ii.next();
412: String arg = CompilerUtils.getString(ann, ARG_ATTR,
413: true);
414: String bundle = CompilerUtils.getString(ann,
415: BUNDLE_NAME_ATTR, true);
416: Integer position = CompilerUtils.getInteger(ann,
417: POSITION_ATTR, true);
418:
419: if (position == null) {
420: position = new Integer(inferredPosition);
421: }
422:
423: if (arg != null) {
424: rule.setArg(arg, false, bundle, position);
425: } else {
426: String argKey = CompilerUtils.getString(ann,
427: ARG_KEY_ATTR, true);
428: if (argKey != null)
429: rule.setArg(argKey, true, bundle, position);
430: }
431:
432: inferredPosition++;
433: }
434: }
435: }
436:
437: protected String getHeaderComment(File mergeFile)
438: throws FatalCompileTimeException {
439: return _strutsApp.getHeaderComment(mergeFile);
440: }
441:
442: public void writeToFile() throws FileNotFoundException,
443: IOException, FatalCompileTimeException,
444: XmlModelWriterException {
445: String outputFilePath = getOutputFileURI();
446: File outputFile = new File(outputFilePath);
447: PrintWriter writer = _env.getFiler().createTextFile(outputFile);
448: try {
449: writeXml(writer, _mergeFile);
450: } finally {
451: writer.close();
452: }
453: }
454:
455: public String getOutputFileURI() {
456: return _strutsApp.getOutputFileURI(_strutsApp
457: .getValidationFilePrefix());
458: }
459: }
|