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.grammar;
020:
021: import org.apache.beehive.netui.compiler.AnnotationMemberType;
022: import org.apache.beehive.netui.compiler.CompilerUtils;
023: import org.apache.beehive.netui.compiler.Diagnostics;
024: import org.apache.beehive.netui.compiler.FlowControllerInfo;
025: import org.apache.beehive.netui.compiler.JpfLanguageConstants;
026: import org.apache.beehive.netui.compiler.RuntimeVersionChecker;
027: import org.apache.beehive.netui.compiler.FatalCompileTimeException;
028: import org.apache.beehive.netui.compiler.typesystem.declaration.*;
029: import org.apache.beehive.netui.compiler.typesystem.env.CoreAnnotationProcessorEnv;
030: import org.apache.beehive.netui.compiler.typesystem.type.DeclaredType;
031: import org.apache.beehive.netui.compiler.typesystem.type.TypeInstance;
032:
033: public class ActionGrammar extends BaseFlowControllerGrammar implements
034: JpfLanguageConstants {
035: public ActionGrammar(CoreAnnotationProcessorEnv env,
036: Diagnostics diags, RuntimeVersionChecker rvc,
037: FlowControllerInfo fcInfo) {
038: super (env, diags, null, rvc, fcInfo);
039:
040: addMemberType(LOGIN_REQUIRED_ATTR, new AnnotationMemberType(
041: null, this ));
042: addMemberType(ROLES_ALLOWED_ATTR, new RolesAllowedType(this ));
043: addMemberType(READONLY_ATTR, new AnnotationMemberType(
044: VERSION_8_SP2_STRING, this ));
045: addMemberType(USE_FORM_BEAN_ATTR, new UseFormBeanType());
046: addMemberType(PREVENT_DOUBLE_SUBMIT_ATTR,
047: new PreventDoubleSubmitType());
048: addMemberType(DO_VALIDATION_ATTR, new DoValidateType());
049:
050: addMemberArrayGrammar(FORWARDS_ATTR, new ForwardGrammar(env,
051: diags, null, rvc, fcInfo));
052: addMemberArrayGrammar(CATCHES_ATTR, new CatchGrammar(env,
053: diags, null, rvc, ACTION_TAG_NAME, fcInfo));
054: addMemberArrayGrammar(VALIDATABLE_PROPERTIES_ATTR,
055: new ValidatablePropertyGrammar(env, diags, rvc));
056: addMemberGrammar(VALIDATION_ERROR_FORWARD_ATTR,
057: new ActionForwardGrammar());
058: }
059:
060: public String[][] getMutuallyExclusiveAttrs() {
061: return null;
062: }
063:
064: public String[][] getRequiredAttrs() {
065: return null;
066: }
067:
068: protected boolean onBeginCheck(AnnotationInstance annotation,
069: AnnotationInstance[] parentAnnotations,
070: MemberDeclaration classMember)
071: throws FatalCompileTimeException {
072: //
073: // First check the form bean type.
074: //
075: TypeInstance argType = getFormBeanType(annotation, classMember);
076: TypeDeclaration argTypeDecl = null;
077:
078: if (!(argType instanceof DeclaredType)) {
079: if (argType != null) {
080: getDiagnostics().addError(annotation,
081: "error.action-invalid-form-bean-type",
082: argType.toString());
083: argType = null;
084: }
085: } else {
086: argTypeDecl = CompilerUtils
087: .getDeclaration((DeclaredType) argType);
088: boolean isClass = argTypeDecl instanceof ClassDeclaration;
089:
090: if (isClass
091: && !CompilerUtils
092: .hasDefaultConstructor(argTypeDecl)) {
093: getDiagnostics()
094: .addError(
095: annotation,
096: "error.action-form-bean-no-default-constructor",
097: argTypeDecl.getQualifiedName());
098: }
099:
100: if (!argTypeDecl.hasModifier(Modifier.PUBLIC)) {
101: getDiagnostics().addError(annotation,
102: "error.action-form-bean-not-public",
103: argTypeDecl.getQualifiedName());
104: }
105:
106: if (isClass && argTypeDecl.getDeclaringType() != null
107: && !argTypeDecl.hasModifier(Modifier.STATIC)) {
108: getDiagnostics().addError(annotation,
109: "error.action-form-bean-not-static",
110: argTypeDecl.getQualifiedName());
111: }
112:
113: //
114: // Give a warning if there is no validationErrorForward annotation and doValidation isn't set to false.
115: //
116: if (CompilerUtils.getAnnotationValue(annotation,
117: VALIDATION_ERROR_FORWARD_ATTR, true) == null
118: && hasValidationAnnotations(argTypeDecl)) {
119: Boolean doValidation = CompilerUtils.getBoolean(
120: annotation, DO_VALIDATION_ATTR, true);
121:
122: if (doValidation == null || doValidation.booleanValue()) {
123: getDiagnostics().addWarning(
124: annotation,
125: "warning.validatable-formbean-no-forward",
126: ANNOTATION_INTERFACE_PREFIX
127: + annotation.getAnnotationType()
128: .getDeclaration()
129: .getSimpleName(),
130: VALIDATION_ERROR_FORWARD_ATTR,
131: argTypeDecl.getQualifiedName());
132: }
133: }
134: }
135:
136: //
137: // Add this action to the FlowControllerInfo.
138: //
139: getFlowControllerInfo().addAction(
140: getActionName(annotation, classMember),
141: argTypeDecl != null ? argTypeDecl.getQualifiedName()
142: : null);
143:
144: //
145: // Check to make sure the 'useFormBean' attribute (reference to a member variable) matches the form declared as
146: // an argument to the action method.
147: //
148: TypeInstance useFormBeanType = getUseFormBeanType(annotation,
149: classMember);
150:
151: if (useFormBeanType != null
152: && useFormBeanType instanceof DeclaredType) {
153: if (argType == null) {
154: String memberFormTypeName = CompilerUtils
155: .getDeclaration((DeclaredType) useFormBeanType)
156: .getQualifiedName();
157: getDiagnostics().addError(annotation,
158: "error.action-mismatched-form",
159: USE_FORM_BEAN_ATTR, memberFormTypeName);
160: } else if (!CompilerUtils.isAssignableFrom(argTypeDecl,
161: useFormBeanType)) {
162: String memberFormTypeName = CompilerUtils
163: .getDeclaration((DeclaredType) useFormBeanType)
164: .getQualifiedName();
165: getDiagnostics().addError(annotation,
166: "error.action-mismatched-form",
167: USE_FORM_BEAN_ATTR, memberFormTypeName);
168: }
169: }
170:
171: return true;
172: }
173:
174: protected String getActionName(AnnotationInstance annotation,
175: MemberDeclaration classMember) {
176: assert classMember instanceof MethodDeclaration : classMember
177: .getClass().getName();
178: return classMember.getSimpleName();
179: }
180:
181: private static boolean hasValidationAnnotations(TypeDeclaration type) {
182: // Could cache this if it's a performance problem.
183:
184: MethodDeclaration[] methods = type.getMethods();
185:
186: for (int i = 0; i < methods.length; i++) {
187: MethodDeclaration method = methods[i];
188: AnnotationInstance[] annotations = method
189: .getAnnotationInstances();
190:
191: for (int j = 0; j < annotations.length; j++) {
192: AnnotationInstance ann = annotations[j];
193: String annotationName = CompilerUtils.getDeclaration(
194: ann.getAnnotationType()).getQualifiedName();
195: int pos = annotationName.indexOf(ANNOTATION_QUALIFIER);
196:
197: if (pos != -1) {
198: if (annotationName.substring(
199: pos + ANNOTATION_QUALIFIER.length())
200: .startsWith("Validat")) {
201: return true;
202: }
203: }
204: }
205: }
206:
207: return false;
208: }
209:
210: protected static TypeInstance getUseFormBeanType(
211: AnnotationInstance annotation, MemberDeclaration classMember) {
212: String formBeanFieldName = CompilerUtils.getString(annotation,
213: USE_FORM_BEAN_ATTR, true);
214:
215: if (formBeanFieldName != null) {
216: FieldDeclaration formBeanField = CompilerUtils.findField(
217: CompilerUtils.getOutermostClass(classMember),
218: formBeanFieldName);
219:
220: if (formBeanField != null) {
221: return CompilerUtils.getGenericBoundsType(formBeanField
222: .getType());
223: }
224: }
225:
226: return null;
227: }
228:
229: protected TypeInstance getFormBeanType(
230: AnnotationInstance annotation, MemberDeclaration classMember) {
231: assert classMember instanceof MethodDeclaration : classMember
232: .getClass().getName();
233: MethodDeclaration method = (MethodDeclaration) classMember;
234: ParameterDeclaration[] parameters = method.getParameters();
235: int nParameters = parameters.length;
236:
237: if (nParameters > 1)
238: getDiagnostics().addError(method,
239: "error.action-method-wrong-arg");
240: if (nParameters > 0)
241: return CompilerUtils.getGenericBoundsType(parameters[0]
242: .getType());
243:
244: return null;
245: }
246:
247: private class DoValidateType extends AnnotationMemberType {
248: public DoValidateType() {
249: super (null, ActionGrammar.this );
250: }
251:
252: public Object onCheck(
253: AnnotationTypeElementDeclaration valueDecl,
254: AnnotationValue member,
255: AnnotationInstance[] parentAnnotations,
256: MemberDeclaration classMember, int annotationArrayIndex) {
257: //
258: // If this value is set to true, there must be a value for validationErrorForward.
259: //
260: if (((Boolean) member.getValue()).booleanValue()) {
261: AnnotationInstance parentAnnotation = parentAnnotations[parentAnnotations.length - 1];
262:
263: if (CompilerUtils.getAnnotation(parentAnnotation,
264: VALIDATION_ERROR_FORWARD_ATTR, true) == null) {
265: addError(
266: member,
267: "error.validate-with-no-validation-error-forward",
268: DO_VALIDATION_ATTR,
269: VALIDATION_ERROR_FORWARD_ATTR);
270: }
271: }
272:
273: return null;
274: }
275: }
276:
277: private class ActionForwardGrammar extends ForwardGrammar {
278: public ActionForwardGrammar() {
279: super (ActionGrammar.this .getEnv(), ActionGrammar.this
280: .getDiagnostics(), null, ActionGrammar.this
281: .getRuntimeVersionChecker(), ActionGrammar.this
282: .getFlowControllerInfo());
283: ExternalPathOrActionType baseForwardType = new ExternalPathOrActionType(
284: false, null, this , ActionGrammar.this
285: .getFlowControllerInfo());
286: addMemberType(PATH_ATTR, new ForwardToExternalPathType(
287: baseForwardType, null, ActionGrammar.this ));
288: }
289: }
290:
291: private class UseFormBeanType extends WritableFieldType {
292: public UseFormBeanType() {
293: super (null, USE_FORM_BEAN_ATTR, null, ActionGrammar.this );
294: }
295:
296: public Object onCheck(
297: AnnotationTypeElementDeclaration valueDecl,
298: AnnotationValue value,
299: AnnotationInstance[] parentAnnotations,
300: MemberDeclaration classMember, int annotationArrayIndex) {
301: FieldDeclaration memberField = (FieldDeclaration) super
302: .onCheck(valueDecl, value, parentAnnotations,
303: classMember, annotationArrayIndex);
304:
305: if (memberField != null) {
306: //
307: // If this action is marked 'readOnly', print a warning about the 'useFormBean' attribute implicitly
308: // modifying member data.
309: //
310: AnnotationInstance parentAnnotation = parentAnnotations[parentAnnotations.length - 1];
311: if (CompilerUtils.getBoolean(parentAnnotation,
312: READONLY_ATTR, false).booleanValue()) {
313: addWarning(value,
314: "warning.use-form-bean-on-readonly-action",
315: READONLY_ATTR, USE_FORM_BEAN_ATTR,
316: memberField.getSimpleName());
317: }
318: }
319:
320: return memberField;
321: }
322: }
323:
324: private class PreventDoubleSubmitType extends AnnotationMemberType {
325: public PreventDoubleSubmitType() {
326: super (null, ActionGrammar.this );
327: }
328:
329: public Object onCheck(
330: AnnotationTypeElementDeclaration valueDecl,
331: AnnotationValue member,
332: AnnotationInstance[] parentAnnotations,
333: MemberDeclaration classMember, int annotationArrayIndex)
334: throws FatalCompileTimeException {
335:
336: // We don't allow the preventDoubleSubmit attribute on the begin action, since this
337: // would make the page flow inaccessible through its URI.
338: AnnotationInstance parentAnn = parentAnnotations[parentAnnotations.length - 1];
339: if (BEGIN_ACTION_NAME.equals(getActionName(parentAnn,
340: classMember))) {
341: addError(member,
342: "error.prevent-double-submit-on-begin",
343: PREVENT_DOUBLE_SUBMIT_ATTR, BEGIN_ACTION_NAME);
344: }
345: return null;
346: }
347: }
348: }
|