0001: /*--
0002:
0003: Copyright (C) 2002-2005 Adrian Price.
0004: All rights reserved.
0005:
0006: Redistribution and use in source and binary forms, with or without
0007: modification, are permitted provided that the following conditions
0008: are met:
0009:
0010: 1. Redistributions of source code must retain the above copyright
0011: notice, this list of conditions, and the following disclaimer.
0012:
0013: 2. Redistributions in binary form must reproduce the above copyright
0014: notice, this list of conditions, and the disclaimer that follows
0015: these conditions in the documentation and/or other materials
0016: provided with the distribution.
0017:
0018: 3. The names "OBE" and "Open Business Engine" must not be used to
0019: endorse or promote products derived from this software without prior
0020: written permission. For written permission, please contact
0021: adrianprice@sourceforge.net.
0022:
0023: 4. Products derived from this software may not be called "OBE" or
0024: "Open Business Engine", nor may "OBE" or "Open Business Engine"
0025: appear in their name, without prior written permission from
0026: Adrian Price (adrianprice@users.sourceforge.net).
0027:
0028: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0029: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0030: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0031: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
0032: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
0033: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
0034: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0035: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
0036: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
0037: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0038: POSSIBILITY OF SUCH DAMAGE.
0039:
0040: For more information on OBE, please see
0041: <http://obe.sourceforge.net/>.
0042:
0043: */
0044:
0045: package org.obe.client.api.xpdl;
0046:
0047: import org.apache.commons.logging.Log;
0048: import org.apache.commons.logging.LogFactory;
0049: import org.obe.xpdl.XPDLNames;
0050: import org.obe.xpdl.model.activity.*;
0051: import org.obe.xpdl.model.application.Application;
0052: import org.obe.xpdl.model.condition.Condition;
0053: import org.obe.xpdl.model.condition.ConditionType;
0054: import org.obe.xpdl.model.data.*;
0055: import org.obe.xpdl.model.ext.Event;
0056: import org.obe.xpdl.model.misc.*;
0057: import org.obe.xpdl.model.participant.Participant;
0058: import org.obe.xpdl.model.pkg.ExternalPackage;
0059: import org.obe.xpdl.model.pkg.PackageHeader;
0060: import org.obe.xpdl.model.pkg.XPDLPackage;
0061: import org.obe.xpdl.model.transition.*;
0062: import org.obe.xpdl.model.workflow.ProcessHeader;
0063: import org.obe.xpdl.model.workflow.WorkflowProcess;
0064: import org.obe.xpdl.parser.dom4j.Dom4JXPDLParser;
0065: import org.obe.xpdl.parser.XPDLParser;
0066: import org.obe.client.api.model.ProcessInstanceAttributes;
0067:
0068: import java.io.FileInputStream;
0069: import java.io.FileReader;
0070: import java.io.InputStream;
0071: import java.io.Reader;
0072: import java.util.*;
0073:
0074: // TODO: use standard XPath expressions for the prefix.
0075:
0076: /**
0077: * Validates an XPDL package bean.
0078: *
0079: * @author Adrian Price
0080: */
0081: public final class PackageValidator implements PackageValidatorMessages {
0082: private static final Log _logger = LogFactory
0083: .getLog(PackageValidator.class);
0084: public static final String GRAPH_CONFORMANCE_MIN_PROP = "graph.conformance.minimum";
0085: private static final String PKG_APPLICATION = "Application(Package)";
0086: private static final String PKG_DATA_FIELD = "DataField(Package)";
0087: private static final String PKG_PARTICIPANT = "Participant(Package)";
0088: private static final String WORKFLOW_FORMAL_PARAMETER = "FormalParameter(Workflow)";
0089: public static final String OTHERWISE_TRANSITION_ALREADY_DEFINED = " an OTHERWISE transition has already been defined";
0090: private static final String[][] DEFAULTS = { {
0091: GRAPH_CONFORMANCE_MIN_PROP,
0092: GraphConformance.NON_BLOCKED.toString() }, };
0093: private static final Set _countryCodes = new TreeSet();
0094: private static final Properties _defaultProps = new Properties();
0095: private Properties _props = new Properties(_defaultProps);
0096:
0097: static {
0098: // Extract valid country codes from the available locales.
0099: Locale[] locales = Locale.getAvailableLocales();
0100: for (int i = 0; i < locales.length; i++) {
0101: String country = locales[i].getCountry();
0102: if (country.length() > 0)
0103: _countryCodes.add(country);
0104: }
0105: }
0106:
0107: private static class ValidationContext {
0108: private XPDLPackage pkg;
0109: private Stack _prefixes = new Stack();
0110: private Map _uniqueIds = new HashMap();
0111: private String _prefix;
0112: private List _errors;
0113: private List _warnings;
0114:
0115: ValidationContext(XPDLPackage pkg) {
0116: this .pkg = pkg;
0117: }
0118:
0119: void popPrefix() {
0120: _prefixes.pop();
0121: generatePrefix();
0122: }
0123:
0124: String peekPrefix() {
0125: return (String) _prefixes.peek();
0126: }
0127:
0128: void pushPrefix(String prefix) {
0129: _prefixes.push(prefix);
0130: generatePrefix();
0131: }
0132:
0133: private void generatePrefix() {
0134: StringBuffer sb = new StringBuffer();
0135: for (Iterator iter = _prefixes.iterator(); iter.hasNext();) {
0136: sb.append('/');
0137: sb.append(iter.next());
0138: }
0139: _prefix = sb.toString();
0140: }
0141:
0142: void checkValidUniqueId(Object src, String prefix,
0143: String priKey, String id, Object obj) {
0144:
0145: checkValidUniqueId(src, prefix, priKey, null, null, id, obj);
0146: }
0147:
0148: void checkValidUniqueId(Object src, String prefix,
0149: String priKey, String secKey, String id, Object obj) {
0150:
0151: checkValidUniqueId(src, prefix, priKey, secKey, null, id,
0152: obj);
0153: }
0154:
0155: void checkValidUniqueId(Object src, String prefix,
0156: String priKey, String secKey, String tertKey,
0157: String id, Object obj) {
0158:
0159: String owner = prefix != null ? prefix : peekPrefix();
0160: if (prefix == null)
0161: prefix = "";
0162: if (id == null || id.trim().length() == 0) {
0163: addError(src, ID_MUST_BE_SPECIFIED,
0164: new Object[] { owner }, prefix
0165: + "/Id must be specified");
0166: } else {
0167: // Make sure this is a valid NMTOKEN string.
0168: if (!isValidNMToken(id)) {
0169: addError(src, INVALID_NMTOKEN,
0170: new Object[] { owner }, prefix
0171: + "/Id is not a valid NMTOKEN");
0172: }
0173:
0174: // Make sure it isn't already in use.
0175: Map uniqueIds = getUniqueIds(priKey);
0176: boolean exists = uniqueIds.containsKey(id);
0177: if (!exists && secKey != null)
0178: exists = getUniqueIds(secKey).containsKey(id);
0179: if (!exists && tertKey != null)
0180: exists = getUniqueIds(tertKey).containsKey(id);
0181: if (exists) {
0182: addError(
0183: src,
0184: ID_ALREADY_DEFINED,
0185: new Object[] { owner },
0186: prefix
0187: + " is already defined. Remove the duplicate entity or assign it a unique ID.");
0188: } else {
0189: uniqueIds.put(id, obj);
0190: }
0191: }
0192: }
0193:
0194: /**
0195: * Checks whether a string is a valid XML Schema
0196: * <a href="http://www.w3.org/TR/2000/WD-xml-2e-20000814#NT-Nmtoken">
0197: * NMTOKEN</a>.
0198: *
0199: * @param id The string to check.
0200: * @return <code>true</code> if it is a valid NMTOKEN.
0201: */
0202: private boolean isValidNMToken(String id) {
0203: // TODO: Handle Unicode CombiningChar and Extender
0204: // CombiningChar ::= [#x0300-#x0345] | [#x0360-#x0361] |
0205: // [#x0483-#x0486] | [#x0591-#x05A1] | [#x05A3-#x05B9] |
0206: // [#x05BB-#x05BD] | #x05BF | [#x05C1-#x05C2] | #x05C4 |
0207: // [#x064B-#x0652] | #x0670 | [#x06D6-#x06DC] | [#x06DD-#x06DF] |
0208: // [#x06E0-#x06E4] | [#x06E7-#x06E8] | [#x06EA-#x06ED] |
0209: // [#x0901-#x0903] | #x093C | [#x093E-#x094C] | #x094D |
0210: // [#x0951-#x0954] | [#x0962-#x0963] | [#x0981-#x0983] | #x09BC |
0211: // #x09BE | #x09BF | [#x09C0-#x09C4] | [#x09C7-#x09C8] |
0212: // [#x09CB-#x09CD] | #x09D7 | [#x09E2-#x09E3] | #x0A02 | #x0A3C |
0213: // #x0A3E | #x0A3F | [#x0A40-#x0A42] | [#x0A47-#x0A48] |
0214: // [#x0A4B-#x0A4D] | [#x0A70-#x0A71] | [#x0A81-#x0A83] | #x0ABC |
0215: // [#x0ABE-#x0AC5] | [#x0AC7-#x0AC9] | [#x0ACB-#x0ACD] |
0216: // [#x0B01-#x0B03] | #x0B3C | [#x0B3E-#x0B43] | [#x0B47-#x0B48] |
0217: // [#x0B4B-#x0B4D] | [#x0B56-#x0B57] | [#x0B82-#x0B83] |
0218: // [#x0BBE-#x0BC2] | [#x0BC6-#x0BC8] | [#x0BCA-#x0BCD] | #x0BD7 |
0219: // [#x0C01-#x0C03] | [#x0C3E-#x0C44] | [#x0C46-#x0C48] |
0220: // [#x0C4A-#x0C4D] | [#x0C55-#x0C56] | [#x0C82-#x0C83] |
0221: // [#x0CBE-#x0CC4] | [#x0CC6-#x0CC8] | [#x0CCA-#x0CCD] |
0222: // [#x0CD5-#x0CD6] | [#x0D02-#x0D03] | [#x0D3E-#x0D43] |
0223: // [#x0D46-#x0D48] | [#x0D4A-#x0D4D] | #x0D57 | #x0E31 |
0224: // [#x0E34-#x0E3A] | [#x0E47-#x0E4E] | #x0EB1 | [#x0EB4-#x0EB9] |
0225: // [#x0EBB-#x0EBC] | [#x0EC8-#x0ECD] | [#x0F18-#x0F19] | #x0F35 |
0226: // #x0F37 | #x0F39 | #x0F3E | #x0F3F | [#x0F71-#x0F84] |
0227: // [#x0F86-#x0F8B] | [#x0F90-#x0F95] | #x0F97 | [#x0F99-#x0FAD] |
0228: // [#x0FB1-#x0FB7] | #x0FB9 | [#x20D0-#x20DC] | #x20E1 |
0229: // [#x302A-#x302F] | #x3099 | #x309A
0230: //
0231: // Extender ::= #x00B7 | #x02D0 | #x02D1 | #x0387 | #x0640 |
0232: // #x0E46 | #x0EC6 | #x3005 | [#x3031-#x3035] | [#x309D-#x309E] |
0233: // [#x30FC-#x30FE
0234: //
0235: // NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
0236: // CombiningChar | Extender
0237: //
0238: // Nmtoken ::= (NameChar)+
0239:
0240: boolean valid = true;
0241: for (int i = 0, n = id.length(); i < n; i++) {
0242: char c = id.charAt(i);
0243: if (!Character.isLetterOrDigit(c) && c != '.'
0244: && c != '-' && c != '_') {
0245:
0246: valid = false;
0247: break;
0248: }
0249: }
0250: return valid;
0251: }
0252:
0253: void checkValidIdRef(Object src, String prefix,
0254: String primaryKey, String refId) {
0255:
0256: checkValidIdRef(src, prefix, primaryKey, null, null, refId);
0257: }
0258:
0259: Object checkValidIdRef(Object src, String prefix,
0260: String primaryKey, String secondaryKey, String refId) {
0261:
0262: return checkValidIdRef(src, prefix, primaryKey,
0263: secondaryKey, null, refId);
0264: }
0265:
0266: Object checkValidIdRef(Object src, String prefix,
0267: String primaryKey, String secondaryKey,
0268: String tertiaryKey, String refId) {
0269:
0270: String owner = prefix != null ? prefix : peekPrefix();
0271: if (prefix == null)
0272: prefix = "";
0273: Object obj = getUniqueIds(primaryKey).get(refId);
0274: if (obj == null && secondaryKey != null)
0275: obj = getUniqueIds(secondaryKey).get(refId);
0276: if (obj == null && tertiaryKey != null)
0277: obj = getUniqueIds(tertiaryKey).get(refId);
0278: if (obj == null) {
0279: addError(src, UNDEFINED_REFERENCE, new Object[] {
0280: owner, primaryKey, refId }, prefix
0281: + " references an undefined " + primaryKey
0282: + '[' + refId + ']');
0283: }
0284: return obj;
0285: }
0286:
0287: void resetUniqueId(String key) {
0288: getUniqueIds(key).clear();
0289: }
0290:
0291: boolean checkNotNull(Object src, String prefix, Object obj) {
0292: if (prefix == null)
0293: prefix = "";
0294: if (obj instanceof String) {
0295: String s = ((String) obj).trim();
0296: if (s.length() == 0)
0297: obj = null;
0298: }
0299: boolean notNull = obj != null;
0300: if (!notNull) {
0301: addError(src, PROPERTY_MISSING, new Object[] {
0302: peekPrefix(), prefix }, prefix
0303: + " must be specified");
0304: }
0305: return notNull;
0306: }
0307:
0308: boolean checkEQ(Object src, String prefix, String property,
0309: int expected, int actual) {
0310:
0311: if (prefix == null)
0312: prefix = "";
0313: boolean eq = actual == expected;
0314: if (!eq) {
0315: addError(src, COUNT_INCORRECT, new Object[] {
0316: peekPrefix(), property, new Integer(expected),
0317: new Integer(actual) }, prefix + ": expected <"
0318: + expected + ">, actual <" + actual + '>');
0319: }
0320: return eq;
0321: }
0322:
0323: void checkGE(Object src, String prefix, String property,
0324: int expected, int actual) {
0325:
0326: if (prefix == null)
0327: prefix = "";
0328: boolean ge = actual >= expected;
0329: if (!ge) {
0330: addError(src, PROPERTY_VALUE_TOO_LOW, new Object[] {
0331: peekPrefix(), property, new Integer(expected),
0332: new Integer(actual) }, prefix + ": expected <"
0333: + expected + ">, actual <" + actual + '>');
0334: }
0335: }
0336:
0337: public void checkAssignable(Object src, String prefix,
0338: Type toType, Type fromType) {
0339:
0340: if (!toType.isAssignableFrom(fromType)) {
0341: addError(src, UNSUPPORTED_TYPE_CONVERSION,
0342: new Object[] { fromType, toType },
0343: "Unsupported type conversion: from <"
0344: + fromType + "> to <" + toType + '>');
0345: }
0346: }
0347:
0348: void addError(Object src, int msgCode, Object[] args, String msg) {
0349: if (_errors == null)
0350: _errors = new ArrayList();
0351: _errors.add(new ValidationError(ValidationError.TYPE_ERROR,
0352: msgCode, args, src, _prefix + msg));
0353: }
0354:
0355: void addWarning(Object src, int msgCode, Object[] args,
0356: String msg) {
0357: if (_warnings == null)
0358: _warnings = new ArrayList();
0359: _warnings.add(new ValidationError(
0360: ValidationError.TYPE_WARNING, msgCode, args, src,
0361: _prefix + msg));
0362: }
0363:
0364: ValidationError[] getErrors() {
0365: return _errors == null ? null : (ValidationError[]) _errors
0366: .toArray(new ValidationError[_errors.size()]);
0367: }
0368:
0369: ValidationError[] getWarnings() {
0370: return _warnings == null ? null
0371: : (ValidationError[]) _warnings
0372: .toArray(new ValidationError[_warnings
0373: .size()]);
0374: }
0375:
0376: private Map getUniqueIds(String key) {
0377: Map ids = (Map) _uniqueIds.get(key);
0378: if (ids == null) {
0379: ids = new HashMap();
0380: _uniqueIds.put(key, ids);
0381: }
0382: return ids;
0383: }
0384: }
0385:
0386: static {
0387: for (int i = 0; i < DEFAULTS.length; i++) {
0388: String[] entry = DEFAULTS[i];
0389: _defaultProps.setProperty(entry[0], entry[1]);
0390: }
0391: }
0392:
0393: public static void main(String[] args) {
0394: try {
0395: XPDLParser parser = new Dom4JXPDLParser();
0396: if (args.length == 0)
0397: usage();
0398: int i = 0;
0399: Properties props = null;
0400: if (args[i].startsWith("-props")) {
0401: String propfile = args[i++];
0402: if (i == args.length)
0403: usage();
0404: props = new Properties();
0405: InputStream in = new FileInputStream(propfile);
0406: props.load(in);
0407: in.close();
0408: }
0409: PackageValidator validator = new PackageValidator(props);
0410: for (; i < args.length; i++) {
0411: String src = args[i];
0412: Reader rdr = new FileReader(src);
0413: XPDLPackage pkg = parser.parse(rdr);
0414: rdr.close();
0415: ValidationError[] errors = validator.validate(pkg,
0416: false);
0417: if (errors == null)
0418: System.out
0419: .println(src + " is a valid XPDL package");
0420: else {
0421: System.err.println(src + " contains errors:\n");
0422: for (int j = 0; j < errors.length; j++)
0423: System.err.println(errors[j].getMessage());
0424: System.err.println("\n" + errors.length
0425: + " errors\n");
0426: }
0427: System.out.println();
0428: }
0429: } catch (Exception e) {
0430: _logger.error(e);
0431: System.exit(1);
0432: }
0433: }
0434:
0435: private static void usage() {
0436: System.out
0437: .println("usage: java org.obe.client.api.xpdl.PackageValidator [-props propfile] file...");
0438: System.exit(1);
0439: }
0440:
0441: public PackageValidator() {
0442: }
0443:
0444: public PackageValidator(Properties props) {
0445: if (props != null)
0446: _props.putAll(props);
0447: }
0448:
0449: public String getProperty(String key) {
0450: return _props.getProperty(key);
0451: }
0452:
0453: public Object setProperty(String key, String value) {
0454: return _props.setProperty(key, value);
0455: }
0456:
0457: public ValidationError[] validate(XPDLPackage pkg,
0458: boolean throwException) throws InvalidPackageException {
0459:
0460: // Make sure all transitions connect to their associated activities.
0461: for (int i = 0, n = pkg.getWorkflowProcess().length; i < n; i++)
0462: pkg.getWorkflowProcess(i).resolveReferences();
0463:
0464: ValidationContext ctx = new ValidationContext(pkg);
0465: checkPackage(ctx);
0466:
0467: ValidationError[] errors = ctx.getErrors();
0468: if (errors != null && throwException) {
0469: RedefinableHeader redefinableHeader = pkg
0470: .getRedefinableHeader();
0471: PublicationStatus publicationStatus = redefinableHeader == null ? null
0472: : redefinableHeader.getPublicationStatus();
0473: if (publicationStatus != PublicationStatus.UNDER_REVISION)
0474: throw new InvalidPackageException(pkg.getId(), errors);
0475: }
0476: return errors;
0477: }
0478:
0479: private void checkPackage(ValidationContext ctx) {
0480: XPDLPackage pkg = ctx.pkg;
0481: ctx.pushPrefix("Package[" + pkg.getId() + ']');
0482: ctx.checkValidUniqueId(pkg, null, XPDLNames.PACKAGE, pkg
0483: .getId(), pkg);
0484: checkPackageHeader(ctx);
0485: checkRedefinableHeader(pkg.getRedefinableHeader(), ctx);
0486: checkConformanceClass(ctx);
0487: checkScript(ctx);
0488: checkExternalPackages(ctx);
0489: checkTypeDeclarations(ctx);
0490: checkParticipants(pkg.getParticipant(), true, ctx);
0491: checkApplications(pkg.getApplication(), true, ctx);
0492: checkDataFields(pkg.getDataField(), true, ctx);
0493: checkWorkflows(ctx);
0494: ctx.popPrefix();
0495: }
0496:
0497: private void checkPackageHeader(ValidationContext ctx) {
0498: ctx.pushPrefix(XPDLNames.PACKAGE_HEADER);
0499: PackageHeader hdr = ctx.pkg.getPackageHeader();
0500: ctx.checkNotNull(hdr, "/XPDLVersion", hdr.getXPDLVersion());
0501: ctx.checkNotNull(hdr, "/Vendor", hdr.getVendor());
0502: ctx.checkNotNull(hdr, "/Created", hdr.getCreated());
0503: ctx.popPrefix();
0504: }
0505:
0506: private void checkRedefinableHeader(RedefinableHeader hdr,
0507: ValidationContext ctx) {
0508:
0509: ctx.pushPrefix(XPDLNames.REDEFINABLE_HEADER);
0510: if (hdr != null) {
0511: // TODO: enable codepage check in JDK1.4 only.
0512: // String codepage = hdr.getCodepage();
0513: // if (codepage != null && Charset.isSupported(codepage)) {
0514: // ctx.addError(hdr, UNSUPPORTED_CODEPAGE, new Object[]{codepage},
0515: // "Unsupported codepage: " + codepage);
0516: // }
0517:
0518: String country = hdr.getCountrykey();
0519: if (country != null && !_countryCodes.contains(country)) {
0520: ctx.addWarning(hdr, UNSUPPORTED_COUNTRY,
0521: new Object[] { country },
0522: "Unsupported country: " + country);
0523: }
0524: }
0525: ctx.popPrefix();
0526: }
0527:
0528: // Ensure a minimum graph conformance level, and that the activity net
0529: // does actually conform to that level.
0530: private void checkConformanceClass(ValidationContext ctx) {
0531: ConformanceClass cc = ctx.pkg.getConformanceClass();
0532: GraphConformance gc = cc == null ? null : cc
0533: .getGraphConformance();
0534: if (gc == null)
0535: gc = GraphConformance.NON_BLOCKED;
0536: String gcl = getProperty(GRAPH_CONFORMANCE_MIN_PROP);
0537: ctx.checkGE(ctx.pkg,
0538: " Graph conformance level must be at least " + gcl,
0539: XPDLNames.GRAPH_CONFORMANCE, GraphConformance.valueOf(
0540: gcl).value(), gc.value());
0541: // TODO: verify activity nets conform to declared graph conformance class.
0542: }
0543:
0544: private void checkScript(ValidationContext ctx) {
0545: Script script = ctx.pkg.getScript();
0546: if (script != null) {
0547: String type = script.getType();
0548: ctx.pushPrefix("Script[" + type + ']');
0549: if (type == null || type.startsWith("text/")
0550: && !type.startsWith("text/x-")
0551: && !type.startsWith("text/xml/x-")) {
0552:
0553: ctx
0554: .addError(
0555: ctx.pkg,
0556: INVALID_SCRIPT,
0557: new Object[] { script },
0558: " is not a valid script declaration. The script type is"
0559: + " an extended MIME Media Type and must start with "
0560: + "\"text/x-\" or \"text/xml/x-\"");
0561: }
0562: ctx.popPrefix();
0563: }
0564: }
0565:
0566: private void checkExternalPackages(ValidationContext ctx) {
0567: ExternalPackage[] pkgs = ctx.pkg.getExternalPackage();
0568: for (int i = 0; i < pkgs.length; i++) {
0569: ExternalPackage pkg = pkgs[i];
0570: ctx.checkNotNull(ctx.pkg,
0571: "/ExternalPackages/ExternalPackage[" + i
0572: + "]/@href", pkg.getHref());
0573: }
0574: }
0575:
0576: private void checkTypeDeclarations(ValidationContext ctx) {
0577: ctx.resetUniqueId(XPDLNames.TYPE_DECLARATION);
0578: TypeDeclaration[] typeDeclarations = ctx.pkg
0579: .getTypeDeclaration();
0580: if (typeDeclarations != null) {
0581: for (int i = 0, n = typeDeclarations.length; i < n; i++)
0582: checkTypeDeclaration(typeDeclarations[i], ctx);
0583: }
0584: }
0585:
0586: private void checkTypeDeclaration(TypeDeclaration typeDecl,
0587: ValidationContext ctx) {
0588:
0589: ctx.pushPrefix("TypeDeclaration[" + typeDecl.getId() + ']');
0590: ctx.checkValidUniqueId(typeDecl, null,
0591: XPDLNames.TYPE_DECLARATION, typeDecl.getId(), typeDecl);
0592: Type type = typeDecl.getType();
0593: if (type == null) {
0594: ctx
0595: .addError(
0596: ctx.pkg,
0597: INVALID_TYPE_DECL,
0598: null,
0599: typeDecl
0600: + " is not a valid type declaration: a type is required.");
0601: }
0602: ctx.popPrefix();
0603: }
0604:
0605: private void checkParticipants(Participant[] participants,
0606: boolean pkgLevel, ValidationContext ctx) {
0607:
0608: String pri = pkgLevel ? PKG_PARTICIPANT : XPDLNames.PARTICIPANT;
0609: String sec = pkgLevel ? null : PKG_PARTICIPANT;
0610: ctx.resetUniqueId(pri);
0611: if (participants != null) {
0612: for (int i = 0, n = participants.length; i < n; i++) {
0613: Participant particip = participants[i];
0614: ctx.checkValidUniqueId(particip, "/Participant["
0615: + particip.getId() + ']', pri, sec, particip
0616: .getId(), particip);
0617: }
0618: }
0619: }
0620:
0621: private void checkApplications(Application[] applications,
0622: boolean pkgLevel, ValidationContext ctx) {
0623:
0624: String pri = pkgLevel ? PKG_APPLICATION : XPDLNames.APPLICATION;
0625: String sec = pkgLevel ? null : PKG_APPLICATION;
0626: ctx.resetUniqueId(pri);
0627: if (applications != null) {
0628: for (int i = 0, n = applications.length; i < n; i++) {
0629: Application app = applications[i];
0630: ctx.pushPrefix("Application[" + app.getId() + ']');
0631: ctx.checkValidUniqueId(app, null, pri, sec,
0632: app.getId(), app);
0633: checkFormalParameters(app.getFormalParameter(), false,
0634: ctx);
0635: ctx.popPrefix();
0636: }
0637: }
0638: }
0639:
0640: private void checkFormalParameters(FormalParameter[] parameters,
0641: boolean wfLevel, ValidationContext ctx) {
0642:
0643: String key = wfLevel ? WORKFLOW_FORMAL_PARAMETER
0644: : XPDLNames.FORMAL_PARAMETER;
0645: ctx.resetUniqueId(key);
0646: if (parameters != null) {
0647: for (int i = 0, n = parameters.length; i < n; i++) {
0648: FormalParameter parm = parameters[i];
0649: ctx.checkValidUniqueId(parm, "/FormalParameter["
0650: + parm.getId() + ']', key, parm.getId(), parm);
0651: }
0652: }
0653: }
0654:
0655: private void checkDataFields(DataField[] dataFields,
0656: boolean pkgLevel, ValidationContext ctx) {
0657:
0658: ctx.resetUniqueId(pkgLevel ? PKG_DATA_FIELD
0659: : XPDLNames.DATA_FIELD);
0660: if (dataFields != null) {
0661: for (int i = 0, n = dataFields.length; i < n; i++)
0662: checkDataField(dataFields[i], pkgLevel, ctx);
0663: }
0664: }
0665:
0666: private void checkDataField(DataField dataField, boolean pkgLevel,
0667: ValidationContext ctx) {
0668:
0669: String pri = pkgLevel ? PKG_DATA_FIELD : XPDLNames.DATA_FIELD;
0670: String sec = pkgLevel ? null : WORKFLOW_FORMAL_PARAMETER;
0671: String tert = pkgLevel ? null : PKG_DATA_FIELD;
0672: String dataFieldId = dataField.getId();
0673: ctx.pushPrefix("DataField[" + dataFieldId + ']');
0674: ctx.checkValidUniqueId(dataField, null, pri, sec, tert,
0675: dataFieldId, dataField);
0676: DataType dataType = dataField.getDataType();
0677: Type type = dataType == null ? null : dataType.getType();
0678: ctx.checkNotNull(dataField, "/Type", type);
0679: ctx.popPrefix();
0680: if (type instanceof DeclaredType) {
0681: ctx.checkValidIdRef(dataField, "/Id",
0682: XPDLNames.TYPE_DECLARATION, ((DeclaredType) type)
0683: .getId());
0684: // } else if (type instanceof BasicType) {
0685: // } else if (type instanceof SchemaType) {
0686: // } else if (type instanceof ExternalReference) {
0687: // } else if (type instanceof RecordType) {
0688: // } else if (type instanceof UnionType) {
0689: // } else if (type instanceof EnumerationType) {
0690: // } else if (type instanceof ArrayType) {
0691: // } else if (type instanceof ListType) {
0692: }
0693: }
0694:
0695: private void checkWorkflows(ValidationContext ctx) {
0696: ctx.resetUniqueId(XPDLNames.WORKFLOW_PROCESS);
0697: WorkflowProcess[] workflowProcesses = ctx.pkg
0698: .getWorkflowProcess();
0699: if (workflowProcesses != null) {
0700: for (int i = 0, n = workflowProcesses.length; i < n; i++)
0701: checkWorkflow(workflowProcesses[i], ctx);
0702: }
0703: }
0704:
0705: private void checkWorkflow(WorkflowProcess workflow,
0706: ValidationContext ctx) {
0707:
0708: ctx.resetUniqueId(XPDLNames.DATA_FIELD);
0709: ctx.resetUniqueId(WORKFLOW_FORMAL_PARAMETER);
0710: ctx.resetUniqueId(XPDLNames.ACTIVITY);
0711: ctx.resetUniqueId(XPDLNames.TRANSITION);
0712: String workflowId = workflow.getId();
0713: ctx.pushPrefix("Workflow[" + workflowId + ']');
0714: ctx.checkValidUniqueId(workflow, null,
0715: XPDLNames.WORKFLOW_PROCESS, workflowId, workflow);
0716: checkProcessHeader(workflow, ctx);
0717: checkRedefinableHeader(workflow.getRedefinableHeader(), ctx);
0718: checkFormalParameters(workflow.getFormalParameter(), true, ctx);
0719: checkDataFields(workflow.getDataField(), false, ctx);
0720: checkParticipants(workflow.getParticipant(), false, ctx);
0721: checkApplications(workflow.getApplication(), false, ctx);
0722: checkActivitySets(workflow.getActivitySet(), ctx);
0723: checkActivities(workflow, workflow.getActivity(), ctx);
0724: checkTransitions(workflow.getTransition(), ctx);
0725: ctx.popPrefix();
0726: }
0727:
0728: private void checkProcessHeader(WorkflowProcess workflow,
0729: ValidationContext ctx) {
0730:
0731: ProcessHeader hdr = workflow.getProcessHeader();
0732: Date validFrom = hdr.getValidFrom();
0733: Date validTo = hdr.getValidTo();
0734: if (validFrom != null && validTo != null
0735: && validTo.before(validFrom)) {
0736: ctx.addError(workflow, INVALID_FROM_TO_DATES, new Object[] {
0737: validFrom, validTo },
0738: "Invalid ValidFrom/ValidTo specification: "
0739: + validFrom + '/' + validTo
0740: + ". ValidFrom must pre-date ValidTo");
0741: }
0742: }
0743:
0744: private void checkActivitySets(ActivitySet[] activitySets,
0745: ValidationContext ctx) {
0746:
0747: ctx.resetUniqueId(XPDLNames.ACTIVITY_SET);
0748: if (activitySets != null) {
0749: for (int i = 0, n = activitySets.length; i < n; i++)
0750: checkActivitySet(activitySets[i], ctx);
0751: }
0752: }
0753:
0754: private void checkActivitySet(ActivitySet activitySet,
0755: ValidationContext ctx) {
0756:
0757: String id = activitySet.getId();
0758: ctx.pushPrefix("ActivitySet[" + id + ']');
0759: ctx.checkValidUniqueId(activitySet, null,
0760: XPDLNames.ACTIVITY_SET, id, activitySet);
0761: checkActivities(activitySet, activitySet.getActivity(), ctx);
0762: checkTransitions(activitySet.getTransition(), ctx);
0763: ctx.popPrefix();
0764: }
0765:
0766: private void checkActivities(Object src, Activity[] activities,
0767: ValidationContext ctx) {
0768:
0769: if (activities != null) {
0770: boolean foundStart = false;
0771: boolean foundExit = false;
0772: for (int i = 0, n = activities.length; i < n; i++) {
0773: Activity activity = activities[i];
0774: checkActivity(activity, ctx);
0775: if (activity.isStartActivity())
0776: foundStart = true;
0777: if (activity.isExitActivity())
0778: foundExit = true;
0779: }
0780: if (!foundStart) {
0781: ctx
0782: .addError(
0783: src,
0784: START_ACTIVITY_REQUIRED,
0785: new Object[] { ctx.peekPrefix() },
0786: " must have at least one start activity (one with no afferent (inbound) transitions)");
0787: }
0788: if (!foundExit) {
0789: ctx
0790: .addError(
0791: src,
0792: EXIT_ACTIVITY_REQUIRED,
0793: new Object[] { ctx.peekPrefix() },
0794: " must have at least one exit activity (one with no efferent (outbound) transitions)");
0795: }
0796: }
0797: }
0798:
0799: private void checkActivity(Activity activity, ValidationContext ctx) {
0800: String activityId = activity.getId();
0801: ctx.pushPrefix("Activity[" + activityId + ']');
0802: ctx.checkValidUniqueId(activity, null, XPDLNames.ACTIVITY,
0803: activityId, activity);
0804: String performer = activity.getPerformer();
0805: if (performer != null) {
0806: StringTokenizer strTok = new StringTokenizer(performer, ",");
0807: while (strTok.hasMoreTokens()) {
0808: ctx.checkValidIdRef(activity, "/Performer",
0809: XPDLNames.PARTICIPANT, PKG_PARTICIPANT, strTok
0810: .nextToken());
0811: }
0812: }
0813: int bodyCount = 0;
0814: BlockActivity blk = activity.getBlockActivity();
0815: if (blk != null) {
0816: String blockId = blk.getBlockId();
0817: if (ctx
0818: .checkNotNull(activity, "/BlockActivity/Id",
0819: blockId)
0820: && blk.getActivitySet() == null) {
0821:
0822: ctx.addError(activity, UNDEFINED_ACTIVITY_SET,
0823: new Object[] { ctx.peekPrefix(), blockId },
0824: "/BlockActivity references an undefined ActivitySet["
0825: + blockId + ']');
0826: }
0827: bodyCount++;
0828: }
0829: Implementation impl = activity.getImplementation();
0830: if (impl != null) {
0831: ctx.pushPrefix(XPDLNames.IMPLEMENTATION);
0832: if (impl instanceof NoImplementation) {
0833: // Nothing to check.
0834: } else if (impl instanceof SubFlow) {
0835: SubFlow subFlow = (SubFlow) impl;
0836: String subFlowId = subFlow.getId();
0837: ctx.pushPrefix("SubFlow[" + subFlowId + ']');
0838: if (ctx.checkNotNull(subFlow, null, subFlowId)) {
0839: boolean found = false;
0840: for (int i = 0, n = ctx.pkg.getWorkflowProcess().length; i < n; i++) {
0841:
0842: WorkflowProcess workflow = ctx.pkg
0843: .getWorkflowProcess(i);
0844: if (workflow.getId().equals(subFlowId)) {
0845: checkActualParameters(subFlow, workflow
0846: .getFormalParameter(), subFlow
0847: .getActualParameter(), ctx);
0848: found = true;
0849: break;
0850: }
0851: }
0852: if (!found) {
0853: ctx.addError(activity, UNDEFINED_SUBPROCESS,
0854: new Object[] { activityId, subFlowId },
0855: " references an undefined WorkflowProcess["
0856: + subFlowId + ']');
0857: }
0858: }
0859: ctx.popPrefix();
0860: } else if (impl instanceof ToolSet) {
0861: ToolSet toolSet = (ToolSet) impl;
0862: for (int j = 0, n = toolSet.getTool().length; j < n; j++) {
0863: Tool tool = toolSet.getTool(j);
0864: String toolId = tool.getId();
0865: if (ctx.checkNotNull(tool, "/Tool/Id", toolId)) {
0866: ctx.pushPrefix("Tool[" + toolId + ']');
0867: Application app = (Application) ctx
0868: .checkValidIdRef(tool, null,
0869: XPDLNames.APPLICATION,
0870: PKG_APPLICATION, toolId);
0871: ctx.checkNotNull(tool, "/Type", tool
0872: .getToolType());
0873: if (app != null) {
0874: ExternalReference extRef = app
0875: .getExternalReference();
0876: if (extRef == null) {
0877: checkActualParameters(tool, app
0878: .getFormalParameter(), tool
0879: .getActualParameter(), ctx);
0880: } else {
0881: checkActualParameters(tool, extRef,
0882: tool.getActualParameter(), ctx);
0883: }
0884: }
0885: ctx.popPrefix();
0886: }
0887: }
0888: }
0889: bodyCount++;
0890: ctx.popPrefix();
0891: }
0892: if (activity.getRoute() != null) {
0893: if (performer != null)
0894: ctx.addError(activity, ROUTE_CANNOT_HAVE_PERFORMER,
0895: new Object[] { activityId },
0896: "/Route cannot have a Performer");
0897: bodyCount++;
0898: }
0899: if (bodyCount != 1) {
0900: ctx
0901: .addError(activity, ACTIVITY_BODY_MISSING,
0902: new Object[] { activityId },
0903: " must include only one of: Route, Implementation, or BlockActivity");
0904: }
0905:
0906: Deadline[] deadlines = activity.getDeadline();
0907: if (deadlines != null) {
0908: Set deadlineExceptions = new HashSet();
0909: ctx.pushPrefix(XPDLNames.DEADLINE);
0910: int syncCount = 0;
0911: for (int i = 0, n = deadlines.length; i < n; i++) {
0912: Deadline deadline = deadlines[i];
0913: // TODO: validate condition.
0914: String condition = deadline.getDeadlineCondition();
0915: ctx.checkNotNull(deadline, "/Condition", condition);
0916: String exceptionName = deadline.getExceptionName();
0917: ctx.checkNotNull(deadline, "/Exception", exceptionName);
0918: deadlineExceptions.add(exceptionName);
0919: if (deadline.getExecutionType() == ExecutionType.SYNCHRONOUS
0920: && ++syncCount == 2) {
0921:
0922: ctx.addError(activity, MAX_ONE_SYNC_DEADLINE,
0923: new Object[] { activityId },
0924: " can only have one synchronous deadline");
0925: }
0926: }
0927: // Check that all deadline exceptions are handled.
0928: Map transitions = activity.getEfferentTransitions();
0929: if (transitions != null) {
0930: for (Iterator iter = transitions.values().iterator(); iter
0931: .hasNext();) {
0932:
0933: Transition transition = (Transition) iter.next();
0934: Condition condition = transition.getCondition();
0935: if (condition != null) {
0936: ConditionType type = condition.getType();
0937: if (type == ConditionType.DEFAULTEXCEPTION) {
0938: deadlineExceptions.clear();
0939: break;
0940: } else if (type == ConditionType.EXCEPTION) {
0941: deadlineExceptions.remove(condition
0942: .getValue());
0943: }
0944: }
0945: if (transition.getExecution() != null
0946: && deadlines.length > 0) {
0947:
0948: ctx.addError(activity, INCOMPATIBLE_TRANSITION,
0949: new Object[] { activityId,
0950: transition.getId() },
0951: " is incompatible with Transition["
0952: + transition.getId()
0953: + "]/ExtendedAttribute["
0954: + XPDLNames.EXECUTION + ']');
0955: }
0956: }
0957: }
0958: for (Iterator iter = deadlineExceptions.iterator(); iter
0959: .hasNext();) {
0960: String exception = (String) iter.next();
0961: ctx
0962: .addError(
0963: activity,
0964: EXCEPTION_NOT_HANDLED,
0965: new Object[] { activityId, exception },
0966: "/ExceptionName["
0967: + exception
0968: + "] is not handled by any efferent (outbound) transition");
0969: }
0970: ctx.popPrefix();
0971: }
0972:
0973: TransitionRestriction[] restrictions = activity
0974: .getTransitionRestriction();
0975: SplitType splitType = null;
0976: boolean joinFound = false;
0977: boolean splitFound = false;
0978: boolean otherwiseFound = false;
0979: boolean conditionFound = false;
0980: boolean nonConditionFound = false;
0981: if (restrictions != null) {
0982: ctx.pushPrefix(XPDLNames.TRANSITION_RESTRICTION);
0983: for (int i = 0, n = restrictions.length; i < n; i++) {
0984: TransitionRestriction restriction = restrictions[i];
0985: Join join = restriction.getJoin();
0986: if (join != null) {
0987: // Only one join is permitted.
0988: if (joinFound) {
0989: ctx
0990: .addError(
0991: activity,
0992: TRANSITION_RESTRICTIONS_ONLY_ONE_JOIN,
0993: new Object[] { activityId },
0994: " TransitionRestrictions can only contain one join.");
0995: }
0996: joinFound = true;
0997: }
0998: Split split = restriction.getSplit();
0999: if (split != null) {
1000: // Only one split is permitted.
1001: if (splitFound) {
1002: ctx
1003: .addError(
1004: activity,
1005: TRANSITION_RESTRICTIONS_ONLY_ONE_SPLIT,
1006: new Object[] { activityId },
1007: " TransitionRestrictions can only contain one split in OBE (XPDL permits more, but does not define the semantics).");
1008: }
1009: splitFound = true;
1010: splitType = split.getType();
1011: ctx.checkNotNull(split, "/Split/Type", splitType);
1012: if (splitType == SplitType.XOR) {
1013: ctx.pushPrefix("Split[XOR]");
1014: String[] transitionRefs = split
1015: .getTransitionReference();
1016: if (transitionRefs == null) {
1017: ctx
1018: .addError(
1019: activity,
1020: SPLIT_MISSING_REFERENCES,
1021: new Object[] { activityId },
1022: " does not reference any transitions");
1023: } else {
1024: // TransitionRefs must refer to valid transitions.
1025: for (int j = 0, p = transitionRefs.length; j < p; j++) {
1026:
1027: String transitionId = transitionRefs[j];
1028: if (!activity.getEfferentTransitions()
1029: .containsKey(transitionId)) {
1030:
1031: ctx
1032: .addError(
1033: activity,
1034: INVALID_TRANSITION_REF,
1035: new Object[] {
1036: activityId,
1037: transitionId },
1038: "/TransitionRef["
1039: + transitionId
1040: + "] references non-efferent (outbound) Transition");
1041: }
1042: }
1043: // Efferent transitions must have TransitionRefs
1044: // (other than exception and event transitions).
1045: for (Iterator iter = activity
1046: .getEfferentTransitions().values()
1047: .iterator(); iter.hasNext();) {
1048:
1049: Transition transition = (Transition) iter
1050: .next();
1051: Condition condition = transition
1052: .getCondition();
1053: ConditionType conditionType = condition == null ? null
1054: : condition.getType();
1055: Event event = transition.getEvent();
1056: if (event == null) {
1057: if (conditionType == null
1058: || conditionType == ConditionType.CONDITION
1059: || conditionType == ConditionType.OTHERWISE) {
1060:
1061: if (conditionType == null)
1062: nonConditionFound = true;
1063: else if (conditionType == ConditionType.CONDITION)
1064: conditionFound = true;
1065:
1066: String transitionId = transition
1067: .getId();
1068: boolean found = false;
1069: for (int k = 0; k < transitionRefs.length; k++) {
1070: if (transitionRefs[k]
1071: .equals(transitionId)) {
1072:
1073: found = true;
1074: break;
1075: }
1076: }
1077: if (!found) {
1078: ctx
1079: .addError(
1080: activity,
1081: TRANSITION_REF_MISSING,
1082: new Object[] {
1083: activityId,
1084: transitionId },
1085: " does not reference Transition["
1086: + transitionId
1087: + ']');
1088: }
1089: }
1090: } else {
1091: if (conditionType == ConditionType.DEFAULTEXCEPTION
1092: || conditionType == ConditionType.EXCEPTION
1093: || conditionType == ConditionType.OTHERWISE) {
1094:
1095: ctx
1096: .addError(
1097: transition,
1098: EVENT_TRANSITION_MISMATCH,
1099: new Object[] {
1100: transition
1101: .getId(),
1102: conditionType },
1103: " OBE event transitions cannot be "
1104: + "used in conjunction with an XPDL "
1105: + "transition type of "
1106: + conditionType);
1107: }
1108: }
1109: if (conditionType == ConditionType.OTHERWISE) {
1110: if (otherwiseFound) {
1111: ctx
1112: .addError(
1113: transition,
1114: OTHERWISE_ALREADY_DEFINED,
1115: new Object[] { activityId },
1116: OTHERWISE_TRANSITION_ALREADY_DEFINED);
1117: }
1118: otherwiseFound = true;
1119: }
1120: }
1121: }
1122: ctx.popPrefix();
1123: } else {
1124: for (Iterator iter = activity
1125: .getEfferentTransitions().values()
1126: .iterator(); iter.hasNext();) {
1127:
1128: Transition transition = (Transition) iter
1129: .next();
1130: Condition condition = transition
1131: .getCondition();
1132: ConditionType transitionType = condition == null ? null
1133: : condition.getType();
1134: if (transitionType == ConditionType.OTHERWISE) {
1135: if (otherwiseFound) {
1136: ctx
1137: .addError(
1138: transition,
1139: OTHERWISE_ALREADY_DEFINED,
1140: new Object[] { activityId },
1141: OTHERWISE_TRANSITION_ALREADY_DEFINED);
1142: }
1143: otherwiseFound = true;
1144: }
1145: }
1146: }
1147: }
1148: }
1149: ctx.popPrefix();
1150: }
1151: if (!joinFound) {
1152: int count = activity.getAfferentTransitions().size();
1153: if (count > 1) {
1154: ctx
1155: .addError(
1156: activity,
1157: JOIN_REQUIRED,
1158: new Object[] { activityId,
1159: new Integer(count) },
1160: " has "
1161: + count
1162: + " afferent (inbound) transitions, and therefore requires a Join");
1163: }
1164: }
1165: if (!splitFound) {
1166: int count = 0;
1167: for (Iterator iter = activity.getEfferentTransitions()
1168: .values().iterator(); iter.hasNext();) {
1169:
1170: Transition transition = (Transition) iter.next();
1171: Condition condition = transition.getCondition();
1172: ConditionType transitionType = condition == null ? null
1173: : condition.getType();
1174: if (transition.getEvent() == null
1175: && (transitionType == null
1176: || transitionType == ConditionType.CONDITION || transitionType == ConditionType.OTHERWISE)) {
1177:
1178: count++;
1179: }
1180: }
1181: if (count > 1) {
1182: ctx
1183: .addError(
1184: activity,
1185: SPLIT_REQUIRED,
1186: new Object[] { activityId,
1187: new Integer(count) },
1188: " has "
1189: + count
1190: + " regular efferent (outbound) transitions, and therefore requires a Split");
1191: }
1192: }
1193:
1194: // XOR-Splits in FULL_BLOCKED workflows must contain an OTHERWISE
1195: // or unconditional transition.
1196: if (splitType == SplitType.XOR && conditionFound
1197: && !nonConditionFound && !otherwiseFound) {
1198: ConformanceClass conformanceClass = activity
1199: .getWorkflowProcess().getPackage()
1200: .getConformanceClass();
1201: boolean fullBlocked = conformanceClass != null
1202: && conformanceClass.getGraphConformance() == GraphConformance.FULL_BLOCKED;
1203:
1204: if (fullBlocked) {
1205: ctx
1206: .addError(
1207: activity,
1208: OTHERWISE_REQUIRED,
1209: new Object[] { activityId },
1210: " is in a FULL_BLOCKED WorkflowProcess and has an "
1211: + "XOR-Split, and therefore requires an OTHERWISE or "
1212: + "unconditional transition.");
1213: }
1214: }
1215: ctx.popPrefix();
1216: }
1217:
1218: private void checkActualParameters(Object src,
1219: ExternalReference extRef, ActualParameter[] actualParms,
1220: ValidationContext ctx) {
1221:
1222: // TODO: Retrieve external reference and check parameters against it.
1223: }
1224:
1225: private void checkActualParameters(Object src,
1226: FormalParameter[] formalParms,
1227: ActualParameter[] actualParms, ValidationContext ctx) {
1228:
1229: int fpCount = formalParms == null ? 0 : formalParms.length;
1230: int apCount = actualParms == null ? 0 : actualParms.length;
1231: boolean ok = ctx.checkEQ(src,
1232: " formal and actual parameter counts differ",
1233: "FormalParameters", fpCount, apCount);
1234: if (ok) {
1235: for (int i = 0; i < fpCount; i++) {
1236: FormalParameter fp = formalParms[i];
1237: ActualParameter ap = actualParms[i];
1238: int pmode = fp.getMode() != null ? fp.getMode().value()
1239: : ParameterMode.IN_INT;
1240: switch (pmode) {
1241: case ParameterMode.IN_INT:
1242: // It's hard to validate an arbitrary script expression,
1243: // especially at design time when we might not have a
1244: // a properly configured WorkFlowEngine and
1245: // EvaluatorFactory available.
1246: break;
1247: case ParameterMode.INOUT_INT:
1248: case ParameterMode.OUT_INT:
1249: String apName = ap.getText();
1250: String prefix = "/ActualParameter[" + fp.getId()
1251: + ']';
1252: Object ref = ProcessInstanceAttributes
1253: .findSystemDataField(apName);
1254: if (ref == null) {
1255: ref = ctx.checkValidIdRef(src, prefix,
1256: XPDLNames.DATA_FIELD,
1257: WORKFLOW_FORMAL_PARAMETER,
1258: PKG_DATA_FIELD, apName);
1259: }
1260: if (ref == null)
1261: break;
1262: DataType dt = null;
1263: if (ref instanceof FormalParameter)
1264: dt = ((FormalParameter) ref).getDataType();
1265: else if (ref instanceof DataField)
1266: dt = ((DataField) ref).getDataType();
1267: if (dt == null) {
1268: fp.getIndex();
1269: ctx.addError(src, DATATYPE_NOT_FOUND,
1270: new Object[] { String.valueOf(i),
1271: apName },
1272: "Unable to determine DataType for ActualParameter["
1273: + i + "]: " + apName);
1274: } else {
1275: ctx.checkAssignable(src, prefix, fp
1276: .getDataType().getType(), dt.getType());
1277: }
1278: break;
1279: }
1280: }
1281: }
1282: }
1283:
1284: private void checkTransitions(Transition[] transitions,
1285: ValidationContext ctx) {
1286:
1287: if (transitions != null) {
1288: for (int i = 0, n = transitions.length; i < n; i++)
1289: checkTransition(transitions[i], ctx);
1290: }
1291: }
1292:
1293: private void checkTransition(Transition transition,
1294: ValidationContext ctx) {
1295:
1296: String transitionId = transition.getId();
1297: ctx.pushPrefix("Transition[" + transitionId + ']');
1298: ctx.checkValidUniqueId(transition, null, XPDLNames.TRANSITION,
1299: transitionId, transition);
1300: if (transition.getFromActivity() == null) {
1301: String fromId = transition.getFrom();
1302: ctx.addError(transition,
1303: TRANSITION_FROM_ACTIVITY_UNDEFINED, new Object[] {
1304: transitionId, fromId },
1305: "/From references an undefined Activity[" + fromId
1306: + ']');
1307: }
1308: if (transition.getToActivity() == null) {
1309: String toId = transition.getTo();
1310: ctx.addError(transition, TRANSITION_TO_ACTIVITY_UNDEFINED,
1311: new Object[] { transitionId, toId },
1312: "/To references an undefined Activity[" + toId
1313: + ']');
1314: }
1315: ctx.popPrefix();
1316: }
1317: }
|