0001: // Copyright 2004, 2005 The Apache Software Foundation
0002: //
0003: // Licensed under the Apache License, Version 2.0 (the "License");
0004: // you may not use this file except in compliance with the License.
0005: // You may obtain a copy of the License at
0006: //
0007: // http://www.apache.org/licenses/LICENSE-2.0
0008: //
0009: // Unless required by applicable law or agreed to in writing, software
0010: // distributed under the License is distributed on an "AS IS" BASIS,
0011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012: // See the License for the specific language governing permissions and
0013: // limitations under the License.
0014:
0015: package org.apache.hivemind.parse;
0016:
0017: import java.io.BufferedInputStream;
0018: import java.io.IOException;
0019: import java.io.InputStream;
0020: import java.util.Enumeration;
0021: import java.util.HashMap;
0022: import java.util.Iterator;
0023: import java.util.Map;
0024: import java.util.Properties;
0025:
0026: import org.apache.commons.logging.Log;
0027: import org.apache.commons.logging.LogFactory;
0028: import org.apache.hivemind.ApplicationRuntimeException;
0029: import org.apache.hivemind.Attribute;
0030: import org.apache.hivemind.ClassResolver;
0031: import org.apache.hivemind.ErrorHandler;
0032: import org.apache.hivemind.Occurances;
0033: import org.apache.hivemind.Resource;
0034: import org.apache.hivemind.impl.AttributeImpl;
0035: import org.apache.hivemind.impl.ElementImpl;
0036: import org.apache.hivemind.internal.Visibility;
0037: import org.apache.hivemind.schema.ElementModel;
0038: import org.apache.hivemind.schema.Rule;
0039: import org.apache.hivemind.schema.impl.AttributeModelImpl;
0040: import org.apache.hivemind.schema.impl.ElementModelImpl;
0041: import org.apache.hivemind.schema.impl.SchemaImpl;
0042: import org.apache.hivemind.schema.rules.CreateObjectRule;
0043: import org.apache.hivemind.schema.rules.InvokeParentRule;
0044: import org.apache.hivemind.schema.rules.PushAttributeRule;
0045: import org.apache.hivemind.schema.rules.PushContentRule;
0046: import org.apache.hivemind.schema.rules.ReadAttributeRule;
0047: import org.apache.hivemind.schema.rules.ReadContentRule;
0048: import org.apache.hivemind.schema.rules.SetModuleRule;
0049: import org.apache.hivemind.schema.rules.SetParentRule;
0050: import org.apache.hivemind.schema.rules.SetPropertyRule;
0051: import org.apache.hivemind.util.IdUtils;
0052: import org.apache.oro.text.regex.MalformedPatternException;
0053: import org.apache.oro.text.regex.Pattern;
0054: import org.apache.oro.text.regex.Perl5Compiler;
0055: import org.apache.oro.text.regex.Perl5Matcher;
0056:
0057: /**
0058: * Used to parse HiveMind module deployment descriptors.
0059: * <p>
0060: * TODO: The parser ignores element content except inside <contribution> and
0061: * <invoke-factory> ... it probably should forbid non-whitespace content.
0062: *
0063: * @author Howard Lewis Ship
0064: */
0065: public final class DescriptorParser extends AbstractParser {
0066: private static final String DEFAULT_SERVICE_MODEL = "singleton";
0067:
0068: private static final Log LOG = LogFactory
0069: .getLog(DescriptorParser.class);
0070:
0071: /**
0072: * States used while parsing the document. Most states correspond to a particular XML element in
0073: * the document. STATE_START is the initial state, before the <module> element is reached.
0074: */
0075: private static final int STATE_START = 0;
0076:
0077: private static final int STATE_MODULE = 1;
0078:
0079: // private static final int STATE_DESCRIPTION = 2;
0080: private static final int STATE_CONFIGURATION_POINT = 3;
0081:
0082: private static final int STATE_CONTRIBUTION = 4;
0083:
0084: private static final int STATE_SERVICE_POINT = 5;
0085:
0086: private static final int STATE_CREATE_INSTANCE = 6;
0087:
0088: private static final int STATE_IMPLEMENTATION = 8;
0089:
0090: /**
0091: * Used for both <schema&;gt; within a <extension-point>, and for
0092: * <parameters-schema> within a <service>.
0093: */
0094: private static final int STATE_SCHEMA = 9;
0095:
0096: private static final int STATE_ELEMENT = 10;
0097:
0098: private static final int STATE_RULES = 11;
0099:
0100: /**
0101: * Used with <invoke-factory> and <interceptor> to collect parameters that will be
0102: * passed to the implementation or interceptor factory service.
0103: */
0104: private static final int STATE_COLLECT_SERVICE_PARAMETERS = 12;
0105:
0106: /**
0107: * Used with the <conversion> element (an alternative to using <rules>. Finds
0108: * <map> elements.
0109: */
0110: private static final int STATE_CONVERSION = 13;
0111:
0112: /**
0113: * Represents building Element hierarchy as a light-wieght DOM.
0114: */
0115:
0116: private static final int STATE_LWDOM = 100;
0117:
0118: /**
0119: * Special state for elements that are not allowed to contain any other elements.
0120: */
0121:
0122: private static final int STATE_NO_CONTENT = 300;
0123:
0124: private static final String SIMPLE_ID = "[a-zA-Z0-9_]+";
0125:
0126: /**
0127: * Format for configuration point ids, service point ids and schema ids. Consists of an optional
0128: * leading underscore, followed by alphanumerics and underscores. Normal naming convention is to
0129: * use a single CamelCase word, like a Java class name.
0130: */
0131: public static final String ID_PATTERN = "^" + SIMPLE_ID + "$";
0132:
0133: /**
0134: * Module ids are a sequence of simple ids seperated by periods. In practice, they look like
0135: * Java package names.
0136: */
0137: public static final String MODULE_ID_PATTERN = "^" + SIMPLE_ID
0138: + "(\\." + SIMPLE_ID + ")*$";
0139:
0140: public static final String VERSION_PATTERN = "[0-9]+(\\.[0-9]+){2}$";
0141:
0142: /**
0143: * Temporary storage of the current {@link org.xml.sax.Attributes}.
0144: */
0145: private Map _attributes = new HashMap();
0146:
0147: /**
0148: * Built from DescriptorParser.properties. Key is element name, value is an instance of
0149: * {@link ElementParseInfo}.
0150: */
0151:
0152: private Map _elementParseInfo = new HashMap();
0153:
0154: private ModuleDescriptor _moduleDescriptor;
0155:
0156: private ErrorHandler _errorHandler;
0157:
0158: private ClassResolver _resolver;
0159:
0160: private Perl5Compiler _compiler;
0161:
0162: private Perl5Matcher _matcher;
0163:
0164: private Map _compiledPatterns;
0165:
0166: /**
0167: * Map of Rule keyed on class name, used with <custom> rules.
0168: */
0169: private final Map _ruleMap = new HashMap();
0170:
0171: private final Map OCCURS_MAP = new HashMap();
0172:
0173: {
0174: OCCURS_MAP.put("0..1", Occurances.OPTIONAL);
0175: OCCURS_MAP.put("1", Occurances.REQUIRED);
0176: OCCURS_MAP.put("1..n", Occurances.ONE_PLUS);
0177: OCCURS_MAP.put("0..n", Occurances.UNBOUNDED);
0178: OCCURS_MAP.put("none", Occurances.NONE);
0179: }
0180:
0181: private final Map VISIBILITY_MAP = new HashMap();
0182:
0183: {
0184: VISIBILITY_MAP.put("public", Visibility.PUBLIC);
0185: VISIBILITY_MAP.put("private", Visibility.PRIVATE);
0186: }
0187:
0188: public DescriptorParser(ErrorHandler errorHandler) {
0189: _errorHandler = errorHandler;
0190:
0191: initializeFromPropertiesFile();
0192: }
0193:
0194: public void begin(String elementName, Map attributes) {
0195: _attributes = attributes;
0196:
0197: switch (getState()) {
0198: case STATE_START:
0199:
0200: beginStart(elementName);
0201: break;
0202:
0203: case STATE_MODULE:
0204:
0205: beginModule(elementName);
0206: break;
0207:
0208: case STATE_CONFIGURATION_POINT:
0209:
0210: beginConfigurationPoint(elementName);
0211: break;
0212:
0213: case STATE_CONTRIBUTION:
0214:
0215: beginContribution(elementName);
0216: break;
0217:
0218: case STATE_LWDOM:
0219:
0220: beginLWDom(elementName);
0221: break;
0222:
0223: case STATE_SERVICE_POINT:
0224:
0225: beginServicePoint(elementName);
0226: break;
0227:
0228: case STATE_IMPLEMENTATION:
0229:
0230: beginImplementation(elementName);
0231: break;
0232:
0233: case STATE_SCHEMA:
0234:
0235: beginSchema(elementName);
0236: break;
0237:
0238: case STATE_ELEMENT:
0239:
0240: beginElement(elementName);
0241: break;
0242:
0243: case STATE_RULES:
0244:
0245: beginRules(elementName);
0246: break;
0247:
0248: case STATE_COLLECT_SERVICE_PARAMETERS:
0249:
0250: beginCollectServiceParameters(elementName);
0251: break;
0252:
0253: case STATE_CONVERSION:
0254:
0255: beginConversion(elementName);
0256: break;
0257:
0258: default:
0259:
0260: unexpectedElement(elementName);
0261: break;
0262: }
0263: }
0264:
0265: /**
0266: * Very similar to {@link #beginContribution(String)}, in that it creates an
0267: * {@link ElementImpl}, adds it as a parameter to the
0268: * {@link AbstractServiceInvocationDescriptor}, then enters STATE_LWDOM to fill in its
0269: * attributes and content.
0270: */
0271:
0272: private void beginCollectServiceParameters(String elementName) {
0273: ElementImpl element = buildLWDomElement(elementName);
0274:
0275: AbstractServiceInvocationDescriptor sid = (AbstractServiceInvocationDescriptor) peekObject();
0276:
0277: sid.addParameter(element);
0278:
0279: push(elementName, element, STATE_LWDOM, false);
0280: }
0281:
0282: /**
0283: * Invoked when a new element starts within STATE_CONFIGURATION_POINT.
0284: */
0285: private void beginConfigurationPoint(String elementName) {
0286: if (elementName.equals("schema")) {
0287: enterEmbeddedConfigurationPointSchema(elementName);
0288: return;
0289: }
0290:
0291: unexpectedElement(elementName);
0292: }
0293:
0294: private void beginContribution(String elementName) {
0295: // This is where things get tricky, the point where we outgrew Jakarta Digester.
0296:
0297: ElementImpl element = buildLWDomElement(elementName);
0298:
0299: ContributionDescriptor ed = (ContributionDescriptor) peekObject();
0300: ed.addElement(element);
0301:
0302: push(elementName, element, STATE_LWDOM, false);
0303: }
0304:
0305: private void beginConversion(String elementName) {
0306: if (elementName.equals("map")) {
0307: ConversionDescriptor cd = (ConversionDescriptor) peekObject();
0308:
0309: AttributeMappingDescriptor amd = new AttributeMappingDescriptor();
0310:
0311: push(elementName, amd, STATE_NO_CONTENT);
0312:
0313: checkAttributes();
0314:
0315: amd.setAttributeName(getAttribute("attribute"));
0316: amd.setPropertyName(getAttribute("property"));
0317:
0318: cd.addAttributeMapping(amd);
0319:
0320: return;
0321: }
0322:
0323: unexpectedElement(elementName);
0324: }
0325:
0326: private void beginElement(String elementName) {
0327: if (elementName.equals("attribute")) {
0328: enterAttribute(elementName);
0329: return;
0330: }
0331:
0332: if (elementName.equals("conversion")) {
0333: enterConversion(elementName);
0334: return;
0335: }
0336:
0337: if (elementName.equals("rules")) {
0338: enterRules(elementName);
0339: return;
0340: }
0341:
0342: // <element> is recursive ... possible, but tricky, if using Digester.
0343:
0344: if (elementName.equals("element")) {
0345: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
0346:
0347: elementModel.addElementModel(enterElement(elementName));
0348: return;
0349: }
0350:
0351: unexpectedElement(elementName);
0352: }
0353:
0354: private void beginImplementation(String elementName) {
0355:
0356: if (elementName.equals("create-instance")) {
0357: enterCreateInstance(elementName);
0358: return;
0359: }
0360:
0361: if (elementName.equals("invoke-factory")) {
0362: enterInvokeFactory(elementName);
0363: return;
0364: }
0365:
0366: if (elementName.equals("interceptor")) {
0367: enterInterceptor(elementName);
0368: return;
0369: }
0370:
0371: unexpectedElement(elementName);
0372: }
0373:
0374: private void beginLWDom(String elementName) {
0375: ElementImpl element = buildLWDomElement(elementName);
0376:
0377: ElementImpl parent = (ElementImpl) peekObject();
0378: parent.addElement(element);
0379:
0380: push(elementName, element, STATE_LWDOM, false);
0381: }
0382:
0383: /**
0384: * Invoked when a new element occurs while in STATE_MODULE.
0385: */
0386: private void beginModule(String elementName) {
0387: if (elementName.equals("configuration-point")) {
0388: enterConfigurationPoint(elementName);
0389:
0390: return;
0391: }
0392:
0393: if (elementName.equals("contribution")) {
0394: enterContribution(elementName);
0395: return;
0396: }
0397:
0398: if (elementName.equals("service-point")) {
0399: enterServicePoint(elementName);
0400:
0401: return;
0402: }
0403:
0404: if (elementName.equals("implementation")) {
0405: enterImplementation(elementName);
0406:
0407: return;
0408: }
0409:
0410: if (elementName.equals("schema")) {
0411: enterSchema(elementName);
0412: return;
0413: }
0414:
0415: if (elementName.equals("sub-module")) {
0416: enterSubModule(elementName);
0417:
0418: return;
0419: }
0420:
0421: if (elementName.equals("dependency")) {
0422: enterDependency(elementName);
0423:
0424: return;
0425: }
0426:
0427: unexpectedElement(elementName);
0428: }
0429:
0430: private void beginRules(String elementName) {
0431:
0432: if (elementName.equals("create-object")) {
0433: enterCreateObject(elementName);
0434: return;
0435: }
0436:
0437: if (elementName.equals("invoke-parent")) {
0438: enterInvokeParent(elementName);
0439: return;
0440: }
0441:
0442: if (elementName.equals("read-attribute")) {
0443: enterReadAttribute(elementName);
0444: return;
0445: }
0446:
0447: if (elementName.equals("read-content")) {
0448: enterReadContent(elementName);
0449: return;
0450: }
0451:
0452: if (elementName.equals("set-module")) {
0453: enterSetModule(elementName);
0454: return;
0455: }
0456:
0457: if (elementName.equals("set-property")) {
0458: enterSetProperty(elementName);
0459: return;
0460: }
0461:
0462: if (elementName.equals("push-attribute")) {
0463: enterPushAttribute(elementName);
0464: return;
0465: }
0466:
0467: if (elementName.equals("push-content")) {
0468: enterPushContent(elementName);
0469: return;
0470: }
0471:
0472: if (elementName.equals("set-parent")) {
0473: enterSetParent(elementName);
0474: return;
0475: }
0476:
0477: if (elementName.equals("custom")) {
0478: enterCustom(elementName);
0479:
0480: return;
0481: }
0482:
0483: unexpectedElement(elementName);
0484: }
0485:
0486: private void beginSchema(String elementName) {
0487: if (elementName.equals("element")) {
0488: SchemaImpl schema = (SchemaImpl) peekObject();
0489:
0490: schema.addElementModel(enterElement(elementName));
0491: return;
0492: }
0493:
0494: unexpectedElement(elementName);
0495: }
0496:
0497: private void beginServicePoint(String elementName) {
0498: if (elementName.equals("parameters-schema")) {
0499: enterParametersSchema(elementName);
0500: return;
0501: }
0502:
0503: // <service-point> allows an super-set of <implementation>.
0504:
0505: beginImplementation(elementName);
0506: }
0507:
0508: /**
0509: * begin outermost element, expect "module".
0510: */
0511: private void beginStart(String elementName) {
0512: if (!elementName.equals("module"))
0513: throw new ApplicationRuntimeException(ParseMessages
0514: .notModule(elementName, getLocation()),
0515: getLocation(), null);
0516:
0517: ModuleDescriptor md = new ModuleDescriptor(_resolver,
0518: _errorHandler);
0519:
0520: push(elementName, md, STATE_MODULE);
0521:
0522: checkAttributes();
0523:
0524: md.setModuleId(getValidatedAttribute("id", MODULE_ID_PATTERN,
0525: "module-id-format"));
0526: md.setVersion(getValidatedAttribute("version", VERSION_PATTERN,
0527: "version-format"));
0528:
0529: String packageName = getAttribute("package");
0530: if (packageName == null)
0531: packageName = md.getModuleId();
0532:
0533: md.setPackageName(packageName);
0534:
0535: // And, this is what we ultimately return from the parse.
0536:
0537: _moduleDescriptor = md;
0538: }
0539:
0540: protected void push(String elementName, Object object, int state) {
0541: if (object instanceof AnnotationHolder)
0542: super .push(elementName, object, state, false);
0543: else
0544: super .push(elementName, object, state, true);
0545: }
0546:
0547: private ElementImpl buildLWDomElement(String elementName) {
0548: ElementImpl result = new ElementImpl();
0549: result.setElementName(elementName);
0550:
0551: Iterator i = _attributes.entrySet().iterator();
0552: while (i.hasNext()) {
0553: Map.Entry entry = (Map.Entry) i.next();
0554:
0555: String name = (String) entry.getKey();
0556: String value = (String) entry.getValue();
0557:
0558: Attribute a = new AttributeImpl(name, value);
0559:
0560: result.addAttribute(a);
0561: }
0562:
0563: return result;
0564: }
0565:
0566: private void checkAttributes() {
0567: checkAttributes(peekElementName());
0568: }
0569:
0570: /**
0571: * Checks that only known attributes are specified. Checks that all required attribute are
0572: * specified.
0573: */
0574: private void checkAttributes(String elementName) {
0575: Iterator i = _attributes.keySet().iterator();
0576:
0577: ElementParseInfo epi = (ElementParseInfo) _elementParseInfo
0578: .get(elementName);
0579:
0580: // A few elements have no attributes at all.
0581:
0582: if (epi == null) {
0583: epi = new ElementParseInfo();
0584: _elementParseInfo.put(elementName, epi);
0585: }
0586:
0587: // First, check that each attribute is in the set of expected attributes.
0588:
0589: while (i.hasNext()) {
0590: String name = (String) i.next();
0591:
0592: if (!epi.isKnown(name))
0593: _errorHandler.error(LOG, ParseMessages
0594: .unknownAttribute(name, getElementPath()),
0595: getLocation(), null);
0596: }
0597:
0598: // Now check that all required attributes have been specified.
0599:
0600: i = epi.getRequiredNames();
0601: while (i.hasNext()) {
0602: String name = (String) i.next();
0603:
0604: if (!_attributes.containsKey(name))
0605: throw new ApplicationRuntimeException(ParseMessages
0606: .requiredAttribute(name, getElementPath(),
0607: getLocation()));
0608: }
0609:
0610: }
0611:
0612: public void end(String elementName) {
0613: switch (getState()) {
0614: case STATE_LWDOM:
0615:
0616: endLWDom();
0617: break;
0618:
0619: case STATE_CONVERSION:
0620:
0621: endConversion();
0622: break;
0623:
0624: case STATE_SCHEMA:
0625:
0626: endSchema();
0627: break;
0628:
0629: default:
0630:
0631: String content = peekContent();
0632:
0633: if (content != null
0634: && (peekObject() instanceof AnnotationHolder))
0635: ((AnnotationHolder) peekObject())
0636: .setAnnotation(content);
0637:
0638: break;
0639: }
0640:
0641: // Pop the top item off the stack.
0642:
0643: pop();
0644: }
0645:
0646: private void endSchema() {
0647: SchemaImpl schema = (SchemaImpl) peekObject();
0648:
0649: schema.setAnnotation(peekContent());
0650:
0651: try {
0652: schema.validateKeyAttributes();
0653: } catch (ApplicationRuntimeException e) {
0654: _errorHandler.error(LOG, ParseMessages
0655: .invalidElementKeyAttribute(schema.getId(), e), e
0656: .getLocation(), e);
0657: }
0658: }
0659:
0660: private void endConversion() {
0661: ConversionDescriptor cd = (ConversionDescriptor) peekObject();
0662:
0663: cd.addRulesForModel();
0664: }
0665:
0666: private void endLWDom() {
0667: ElementImpl element = (ElementImpl) peekObject();
0668: element.setContent(peekContent());
0669: }
0670:
0671: private void enterAttribute(String elementName) {
0672: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
0673:
0674: AttributeModelImpl attributeModel = new AttributeModelImpl();
0675:
0676: push(elementName, attributeModel, STATE_NO_CONTENT);
0677:
0678: checkAttributes();
0679:
0680: attributeModel.setName(getAttribute("name"));
0681: attributeModel.setRequired(getBooleanAttribute("required",
0682: false));
0683: attributeModel.setUnique(getBooleanAttribute("unique", false));
0684: attributeModel
0685: .setTranslator(getAttribute("translator", "smart"));
0686:
0687: elementModel.addAttributeModel(attributeModel);
0688: }
0689:
0690: private void enterConfigurationPoint(String elementName) {
0691: ModuleDescriptor md = (ModuleDescriptor) peekObject();
0692:
0693: ConfigurationPointDescriptor cpd = new ConfigurationPointDescriptor();
0694:
0695: push(elementName, cpd, STATE_CONFIGURATION_POINT);
0696:
0697: checkAttributes();
0698:
0699: cpd.setId(getValidatedAttribute("id", ID_PATTERN, "id-format"));
0700:
0701: Occurances count = (Occurances) getEnumAttribute("occurs",
0702: OCCURS_MAP);
0703:
0704: if (count != null)
0705: cpd.setCount(count);
0706:
0707: Visibility visibility = (Visibility) getEnumAttribute(
0708: "visibility", VISIBILITY_MAP);
0709:
0710: if (visibility != null)
0711: cpd.setVisibility(visibility);
0712:
0713: cpd.setContributionsSchemaId(getAttribute("schema-id"));
0714:
0715: md.addConfigurationPoint(cpd);
0716: }
0717:
0718: private void enterContribution(String elementName) {
0719: ModuleDescriptor md = (ModuleDescriptor) peekObject();
0720:
0721: ContributionDescriptor cd = new ContributionDescriptor();
0722:
0723: push(elementName, cd, STATE_CONTRIBUTION);
0724:
0725: checkAttributes();
0726:
0727: cd.setConfigurationId(getAttribute("configuration-id"));
0728: cd.setConditionalExpression(getAttribute("if"));
0729:
0730: md.addContribution(cd);
0731: }
0732:
0733: private void enterConversion(String elementName) {
0734: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
0735:
0736: ConversionDescriptor cd = new ConversionDescriptor(
0737: _errorHandler, elementModel);
0738:
0739: push(elementName, cd, STATE_CONVERSION);
0740:
0741: checkAttributes();
0742:
0743: cd.setClassName(getAttribute("class"));
0744:
0745: String methodName = getAttribute("parent-method");
0746:
0747: if (methodName != null)
0748: cd.setParentMethodName(methodName);
0749:
0750: elementModel.addRule(cd);
0751: }
0752:
0753: private void enterCreateInstance(String elementName) {
0754: AbstractServiceDescriptor sd = (AbstractServiceDescriptor) peekObject();
0755: CreateInstanceDescriptor cid = new CreateInstanceDescriptor();
0756:
0757: push(elementName, cid, STATE_CREATE_INSTANCE);
0758:
0759: checkAttributes();
0760:
0761: cid.setInstanceClassName(getAttribute("class"));
0762:
0763: String model = getAttribute("model", DEFAULT_SERVICE_MODEL);
0764:
0765: cid.setServiceModel(model);
0766:
0767: sd.setInstanceBuilder(cid);
0768:
0769: }
0770:
0771: private void enterCreateObject(String elementName) {
0772: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
0773: CreateObjectRule rule = new CreateObjectRule();
0774: push(elementName, rule, STATE_NO_CONTENT);
0775:
0776: checkAttributes();
0777:
0778: rule.setClassName(getAttribute("class"));
0779:
0780: elementModel.addRule(rule);
0781: }
0782:
0783: private void enterCustom(String elementName) {
0784: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
0785:
0786: // Don't know what it is going to be, yet.
0787:
0788: push(elementName, null, STATE_NO_CONTENT);
0789:
0790: checkAttributes();
0791:
0792: String ruleClassName = getAttribute("class");
0793:
0794: Rule rule = getCustomRule(ruleClassName);
0795:
0796: elementModel.addRule(rule);
0797: }
0798:
0799: /**
0800: * Pushes STATE_ELEMENT onto the stack and creates and returns the {@link ElementModelImpl} it
0801: * creates.
0802: */
0803: private ElementModel enterElement(String elementName) {
0804: ElementModelImpl result = new ElementModelImpl();
0805:
0806: push(elementName, result, STATE_ELEMENT);
0807:
0808: checkAttributes();
0809:
0810: result.setElementName(getAttribute("name"));
0811: result.setKeyAttribute(getAttribute("key-attribute"));
0812: result.setContentTranslator(getAttribute("content-translator"));
0813:
0814: return result;
0815: }
0816:
0817: private void enterEmbeddedConfigurationPointSchema(
0818: String elementName) {
0819: ConfigurationPointDescriptor cpd = (ConfigurationPointDescriptor) peekObject();
0820:
0821: SchemaImpl schema = new SchemaImpl();
0822:
0823: push(elementName, schema, STATE_SCHEMA);
0824:
0825: if (cpd.getContributionsSchemaId() != null) {
0826: cpd.setContributionsSchemaId(null);
0827: cpd.setContributionsSchema(schema);
0828: _errorHandler
0829: .error(LOG, ParseMessages
0830: .multipleContributionsSchemas(cpd.getId(),
0831: schema.getLocation()), schema
0832: .getLocation(), null);
0833: } else
0834: cpd.setContributionsSchema(schema);
0835:
0836: checkAttributes("schema{embedded}");
0837: }
0838:
0839: private void enterParametersSchema(String elementName) {
0840: ServicePointDescriptor spd = (ServicePointDescriptor) peekObject();
0841: SchemaImpl schema = new SchemaImpl();
0842:
0843: push(elementName, schema, STATE_SCHEMA);
0844:
0845: checkAttributes();
0846:
0847: if (spd.getParametersSchemaId() != null) {
0848: spd.setParametersSchemaId(null);
0849: spd.setParametersSchema(schema);
0850: _errorHandler
0851: .error(LOG, ParseMessages
0852: .multipleParametersSchemas(spd.getId(),
0853: schema.getLocation()), schema
0854: .getLocation(), null);
0855: } else
0856: spd.setParametersSchema(schema);
0857: }
0858:
0859: private void enterImplementation(String elementName) {
0860: ModuleDescriptor md = (ModuleDescriptor) peekObject();
0861:
0862: ImplementationDescriptor id = new ImplementationDescriptor();
0863:
0864: push(elementName, id, STATE_IMPLEMENTATION);
0865:
0866: checkAttributes();
0867:
0868: id.setServiceId(getAttribute("service-id"));
0869: id.setConditionalExpression(getAttribute("if"));
0870:
0871: md.addImplementation(id);
0872: }
0873:
0874: private void enterInterceptor(String elementName) {
0875: AbstractServiceDescriptor sd = (AbstractServiceDescriptor) peekObject();
0876: InterceptorDescriptor id = new InterceptorDescriptor();
0877:
0878: push(elementName, id, STATE_COLLECT_SERVICE_PARAMETERS);
0879:
0880: checkAttributes();
0881:
0882: id.setFactoryServiceId(getAttribute("service-id"));
0883:
0884: id.setBefore(getAttribute("before"));
0885: id.setAfter(getAttribute("after"));
0886: id.setName(getAttribute("name"));
0887: sd.addInterceptor(id);
0888:
0889: }
0890:
0891: private void enterInvokeFactory(String elementName) {
0892: AbstractServiceDescriptor sd = (AbstractServiceDescriptor) peekObject();
0893: InvokeFactoryDescriptor ifd = new InvokeFactoryDescriptor();
0894:
0895: push(elementName, ifd, STATE_COLLECT_SERVICE_PARAMETERS);
0896:
0897: checkAttributes();
0898:
0899: ifd.setFactoryServiceId(getAttribute("service-id",
0900: "hivemind.BuilderFactory"));
0901:
0902: String model = getAttribute("model", DEFAULT_SERVICE_MODEL);
0903:
0904: ifd.setServiceModel(model);
0905:
0906: // TODO: Check if instanceBuilder already set
0907:
0908: sd.setInstanceBuilder(ifd);
0909:
0910: }
0911:
0912: private void enterInvokeParent(String elementName) {
0913: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
0914: InvokeParentRule rule = new InvokeParentRule();
0915:
0916: push(elementName, rule, STATE_NO_CONTENT);
0917:
0918: checkAttributes();
0919:
0920: rule.setMethodName(getAttribute("method"));
0921:
0922: if (_attributes.containsKey("depth"))
0923: rule.setDepth(getIntAttribute("depth"));
0924:
0925: elementModel.addRule(rule);
0926: }
0927:
0928: private void enterReadAttribute(String elementName) {
0929: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
0930: ReadAttributeRule rule = new ReadAttributeRule();
0931:
0932: push(elementName, rule, STATE_NO_CONTENT);
0933:
0934: checkAttributes();
0935:
0936: rule.setPropertyName(getAttribute("property"));
0937: rule.setAttributeName(getAttribute("attribute"));
0938: rule.setSkipIfNull(getBooleanAttribute("skip-if-null", true));
0939: rule.setTranslator(getAttribute("translator"));
0940:
0941: elementModel.addRule(rule);
0942: }
0943:
0944: private void enterReadContent(String elementName) {
0945: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
0946: ReadContentRule rule = new ReadContentRule();
0947:
0948: push(elementName, rule, STATE_NO_CONTENT);
0949:
0950: checkAttributes();
0951:
0952: rule.setPropertyName(getAttribute("property"));
0953:
0954: elementModel.addRule(rule);
0955: }
0956:
0957: private void enterRules(String elementName) {
0958: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
0959:
0960: push(elementName, elementModel, STATE_RULES);
0961:
0962: }
0963:
0964: private void enterSchema(String elementName) {
0965: SchemaImpl schema = new SchemaImpl();
0966:
0967: push(elementName, schema, STATE_SCHEMA);
0968:
0969: checkAttributes();
0970:
0971: String id = getValidatedAttribute("id", ID_PATTERN, "id-format");
0972:
0973: schema.setId(id);
0974:
0975: Visibility visibility = (Visibility) getEnumAttribute(
0976: "visibility", VISIBILITY_MAP);
0977:
0978: if (visibility != null)
0979: schema.setVisibility(visibility);
0980:
0981: _moduleDescriptor.addSchema(schema);
0982: }
0983:
0984: private void enterServicePoint(String elementName) {
0985: ModuleDescriptor md = (ModuleDescriptor) peekObject();
0986:
0987: ServicePointDescriptor spd = new ServicePointDescriptor();
0988:
0989: push(elementName, spd, STATE_SERVICE_POINT);
0990:
0991: checkAttributes();
0992:
0993: String id = getValidatedAttribute("id", ID_PATTERN, "id-format");
0994:
0995: // Get the interface name, and default it to the service id if omitted.
0996:
0997: String interfaceAttribute = getAttribute("interface", id);
0998:
0999: // Qualify the interface name with the defined package name (which will
1000: // often implicitly or explicitly match the module id).
1001:
1002: String interfaceName = IdUtils.qualify(_moduleDescriptor
1003: .getPackageName(), interfaceAttribute);
1004:
1005: spd.setId(id);
1006:
1007: spd.setInterfaceClassName(interfaceName);
1008:
1009: spd.setParametersSchemaId(getAttribute("parameters-schema-id"));
1010:
1011: Occurances count = (Occurances) getEnumAttribute(
1012: "parameters-occurs", OCCURS_MAP);
1013:
1014: if (count != null)
1015: spd.setParametersCount(count);
1016:
1017: Visibility visibility = (Visibility) getEnumAttribute(
1018: "visibility", VISIBILITY_MAP);
1019:
1020: if (visibility != null)
1021: spd.setVisibility(visibility);
1022:
1023: md.addServicePoint(spd);
1024: }
1025:
1026: private void enterSetModule(String elementName) {
1027: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
1028: SetModuleRule rule = new SetModuleRule();
1029:
1030: push(elementName, rule, STATE_NO_CONTENT);
1031:
1032: checkAttributes();
1033:
1034: rule.setPropertyName(getAttribute("property"));
1035:
1036: elementModel.addRule(rule);
1037: }
1038:
1039: private void enterSetParent(String elementName) {
1040: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
1041: SetParentRule rule = new SetParentRule();
1042:
1043: push(elementName, rule, STATE_NO_CONTENT);
1044:
1045: checkAttributes();
1046:
1047: rule.setPropertyName(getAttribute("property"));
1048:
1049: elementModel.addRule(rule);
1050: }
1051:
1052: private void enterSetProperty(String elementName) {
1053: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
1054:
1055: SetPropertyRule rule = new SetPropertyRule();
1056:
1057: push(elementName, rule, STATE_NO_CONTENT);
1058:
1059: checkAttributes();
1060:
1061: rule.setPropertyName(getAttribute("property"));
1062: rule.setValue(getAttribute("value"));
1063:
1064: elementModel.addRule(rule);
1065: }
1066:
1067: private void enterPushAttribute(String elementName) {
1068: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
1069:
1070: PushAttributeRule rule = new PushAttributeRule();
1071:
1072: push(elementName, rule, STATE_NO_CONTENT);
1073:
1074: checkAttributes();
1075:
1076: rule.setAttributeName(getAttribute("attribute"));
1077:
1078: elementModel.addRule(rule);
1079: }
1080:
1081: private void enterPushContent(String elementName) {
1082: ElementModelImpl elementModel = (ElementModelImpl) peekObject();
1083:
1084: PushContentRule rule = new PushContentRule();
1085:
1086: push(elementName, rule, STATE_NO_CONTENT);
1087:
1088: checkAttributes();
1089:
1090: elementModel.addRule(rule);
1091: }
1092:
1093: private void enterSubModule(String elementName) {
1094: ModuleDescriptor md = (ModuleDescriptor) peekObject();
1095:
1096: SubModuleDescriptor smd = new SubModuleDescriptor();
1097:
1098: push(elementName, smd, STATE_NO_CONTENT);
1099:
1100: checkAttributes();
1101:
1102: Resource descriptor = getResource().getRelativeResource(
1103: getAttribute("descriptor"));
1104:
1105: smd.setDescriptor(descriptor);
1106:
1107: md.addSubModule(smd);
1108: }
1109:
1110: private void enterDependency(String elementName) {
1111: ModuleDescriptor md = (ModuleDescriptor) peekObject();
1112:
1113: DependencyDescriptor dd = new DependencyDescriptor();
1114:
1115: push(elementName, dd, STATE_NO_CONTENT);
1116:
1117: checkAttributes();
1118:
1119: dd.setModuleId(getAttribute("module-id"));
1120: dd.setVersion(getAttribute("version"));
1121:
1122: md.addDependency(dd);
1123: }
1124:
1125: private String getAttribute(String name) {
1126: return (String) _attributes.get(name);
1127: }
1128:
1129: private String getAttribute(String name, String defaultValue) {
1130: String result = (String) _attributes.get(name);
1131:
1132: if (result == null)
1133: result = defaultValue;
1134:
1135: return result;
1136: }
1137:
1138: private String getValidatedAttribute(String name, String pattern,
1139: String formatKey) {
1140: String result = getAttribute(name);
1141:
1142: if (!validateFormat(result, pattern))
1143: _errorHandler.error(LOG, ParseMessages
1144: .invalidAttributeFormat(name, result,
1145: getElementPath(), formatKey),
1146: getLocation(), null);
1147:
1148: return result;
1149: }
1150:
1151: private boolean validateFormat(String input, String pattern) {
1152: if (_compiler == null) {
1153: _compiler = new Perl5Compiler();
1154: _matcher = new Perl5Matcher();
1155: _compiledPatterns = new HashMap();
1156: }
1157:
1158: Pattern compiled = (Pattern) _compiledPatterns.get(pattern);
1159: if (compiled == null) {
1160:
1161: try {
1162: compiled = _compiler.compile(pattern);
1163: } catch (MalformedPatternException ex) {
1164: throw new ApplicationRuntimeException(ex);
1165: }
1166:
1167: _compiledPatterns.put(pattern, compiled);
1168: }
1169:
1170: return _matcher.matches(input, compiled);
1171: }
1172:
1173: private boolean getBooleanAttribute(String name,
1174: boolean defaultValue) {
1175: String value = getAttribute(name);
1176:
1177: if (value == null)
1178: return defaultValue;
1179:
1180: if (value.equals("true"))
1181: return true;
1182:
1183: if (value.equals("false"))
1184: return false;
1185:
1186: _errorHandler.error(LOG, ParseMessages.booleanAttribute(value,
1187: name, getElementPath()), getLocation(), null);
1188:
1189: return defaultValue;
1190: }
1191:
1192: private Rule getCustomRule(String ruleClassName) {
1193: Rule result = (Rule) _ruleMap.get(ruleClassName);
1194:
1195: if (result == null) {
1196: result = instantiateRule(ruleClassName);
1197:
1198: _ruleMap.put(ruleClassName, result);
1199: }
1200:
1201: return result;
1202: }
1203:
1204: /**
1205: * Gets the value for the attribute and uses the Map to translate it to an object value. Returns
1206: * the object value if succesfully translated. Returns null if unsuccesful. If a value is
1207: * provided that isn't a key of the map, and error is logged and null is returned.
1208: */
1209: private Object getEnumAttribute(String name, Map translations) {
1210: String value = getAttribute(name);
1211:
1212: if (value == null)
1213: return null;
1214:
1215: Object result = translations.get(value);
1216:
1217: if (result == null)
1218: _errorHandler.error(LOG, ParseMessages
1219: .invalidAttributeValue(value, name,
1220: getElementPath()), getLocation(), null);
1221:
1222: return result;
1223: }
1224:
1225: private int getIntAttribute(String name) {
1226: String value = getAttribute(name);
1227:
1228: try {
1229: return Integer.parseInt(value);
1230: } catch (NumberFormatException ex) {
1231: _errorHandler.error(LOG, ParseMessages.invalidNumericValue(
1232: value, name, getElementPath()), getLocation(), ex);
1233:
1234: return 0;
1235: }
1236: }
1237:
1238: private void initializeFromProperties(Properties p) {
1239: Enumeration e = p.propertyNames();
1240:
1241: while (e.hasMoreElements()) {
1242: String key = (String) e.nextElement();
1243: String value = p.getProperty(key);
1244:
1245: initializeFromProperty(key, value);
1246: }
1247: }
1248:
1249: /**
1250: * Invoked from the constructor to read the properties file that defines certain aspects of the
1251: * operation of the parser.
1252: */
1253: private void initializeFromPropertiesFile() {
1254: Properties p = new Properties();
1255:
1256: try {
1257:
1258: InputStream propertiesIn = getClass().getResourceAsStream(
1259: "DescriptorParser.properties");
1260: InputStream bufferedIn = new BufferedInputStream(
1261: propertiesIn);
1262:
1263: p.load(bufferedIn);
1264:
1265: bufferedIn.close();
1266: } catch (IOException ex) {
1267: _errorHandler.error(LOG, ParseMessages
1268: .unableToInitialize(ex), null, ex);
1269: }
1270:
1271: initializeFromProperties(p);
1272: }
1273:
1274: private void initializeFromProperty(String key, String value) {
1275: if (key.startsWith("required.")) {
1276: initializeRequired(key, value);
1277: return;
1278: }
1279:
1280: }
1281:
1282: private void initializeRequired(String key, String value) {
1283: boolean required = value.equals("true");
1284:
1285: int lastdotx = key.lastIndexOf('.');
1286:
1287: String elementName = key.substring(9, lastdotx);
1288: String attributeName = key.substring(lastdotx + 1);
1289:
1290: ElementParseInfo epi = (ElementParseInfo) _elementParseInfo
1291: .get(elementName);
1292:
1293: if (epi == null) {
1294: epi = new ElementParseInfo();
1295: _elementParseInfo.put(elementName, epi);
1296: }
1297:
1298: epi.addAttribute(attributeName, required);
1299: }
1300:
1301: private Rule instantiateRule(String ruleClassName) {
1302: try {
1303: Class ruleClass = _resolver.findClass(ruleClassName);
1304:
1305: return (Rule) ruleClass.newInstance();
1306: } catch (Exception ex) {
1307: throw new ApplicationRuntimeException(ParseMessages
1308: .badRuleClass(ruleClassName, getLocation(), ex),
1309: getLocation(), ex);
1310: }
1311: }
1312:
1313: /** @since 1.1 */
1314: public void initialize(Resource resource, ClassResolver resolver) {
1315: initializeParser(resource, STATE_START);
1316:
1317: _resolver = resolver;
1318: }
1319:
1320: /** @since 1.1 */
1321: public ModuleDescriptor getModuleDescriptor() {
1322: return _moduleDescriptor;
1323: }
1324:
1325: /** @since 1.1 */
1326: public void reset() {
1327: super.resetParser();
1328:
1329: _moduleDescriptor = null;
1330: _attributes.clear();
1331: _resolver = null;
1332: }
1333: }
|