Coverage Report - com.sdmetrics.model.MetaModel - www.sdmetrics.com
 
Classes in this File Line Coverage Branch Coverage Complexity
MetaModel
100%
7/7
N/A
2,7
MetaModel$MetaModelParser
100%
61/61
100%
34/34
2,7
 
 1  
 /*
 2  
  * SDMetrics Open Core for UML design measurement
 3  
  * Copyright (c) Juergen Wuest
 4  
  * To contact the author, see <http://www.sdmetrics.com/Contact.html>.
 5  
  * 
 6  
  * This file is part of the SDMetrics Open Core.
 7  
  * 
 8  
  * SDMetrics Open Core is free software: you can redistribute it and/or modify
 9  
  * it under the terms of the GNU Affero General Public License as
 10  
  * published by the Free Software Foundation, either version 3 of the
 11  
  * License, or (at your option) any later version.
 12  
     
 13  
  * SDMetrics Open Core is distributed in the hope that it will be useful,
 14  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16  
  * GNU Affero General Public License for more details.
 17  
  *
 18  
  * You should have received a copy of the GNU Affero General Public License
 19  
  * along with SDMetrics Open Core.  If not, see <http://www.gnu.org/licenses/>.
 20  
  *
 21  
  */
 22  
 package com.sdmetrics.model;
 23  
 
 24  
 import java.util.Iterator;
 25  
 import java.util.LinkedHashMap;
 26  
 import java.util.Map;
 27  
 
 28  
 import org.xml.sax.Attributes;
 29  
 import org.xml.sax.SAXException;
 30  
 import org.xml.sax.helpers.DefaultHandler;
 31  
 
 32  
 import com.sdmetrics.util.SAXHandler;
 33  
 
 34  
 /**
 35  
  * Represents an SDMetrics metamodel. The metamodel defines the available model
 36  
  * element types and their attributes.
 37  
  * <p>
 38  
  * The class parses the metamodel definition file, manages the set of
 39  
  * {@link MetaModelElement} instances that constitute the metamodel, and
 40  
  * provides access to them.
 41  
  */
 42  448
 public class MetaModel implements Iterable<MetaModelElement> {
 43  
 
 44  
         /** The name of the top level XML element in the metamodel definition file. */
 45  
         public static final String TL_ELEMENT = "sdmetricsmetamodel";
 46  
         /** The name of the base element type. */
 47  
         public static final String BASE_ELEMENT = "sdmetricsbase";
 48  
 
 49  
         /**
 50  
          * Directory of the element types that constitute this metamodel. Allows
 51  
          * lookup of the element types by their name.
 52  
          */
 53  5441
         private final Map<String, MetaModelElement> elementMap = 
 54  224
                         new LinkedHashMap<String, MetaModelElement>();
 55  
 
 56  
         /**
 57  
          * Retrieves an iterator over the element types in this metamodel. Types are
 58  
          * returned in the order in which they were added/defined in the metamodel
 59  
          * definition file.
 60  
          * 
 61  
          * @return Metamodel element iterator
 62  
          */
 63  
         @Override
 64  
         public Iterator<MetaModelElement> iterator() {
 65  775
                 return elementMap.values().iterator();
 66  
         }
 67  
 
 68  
         /**
 69  
          * Gets a SAX handler to populate this metamodel with the contents of a
 70  
          * metamodel definition file.
 71  
          * 
 72  
          * @return SAX handler to parse the metamodel definition file
 73  
          */
 74  
         public DefaultHandler getSAXParserHandler() {
 75  223
                 return new MetaModelParser();
 76  
         }
 77  
 
 78  
         /**
 79  
          * Retrieves a metamodel element by its type name.
 80  
          * 
 81  
          * @param typeName The type name of the metamodel element.
 82  
          * @return The metamodel element with that name or <code>null</code> if
 83  
          *         there is no element of that name.
 84  
          */
 85  
         public MetaModelElement getType(String typeName) {
 86  12593
                 return elementMap.get(typeName);
 87  
         }
 88  
 
 89  
         /**
 90  
          * Gets the number of element types defined by this metamodel.
 91  
          * 
 92  
          * @return The number of types in this metamodel.
 93  
          */
 94  
         int getNumberOfTypes() {
 95  161
                 return elementMap.size();
 96  
         }
 97  
 
 98  
         /**
 99  
          * SAX handler to parse a metamodel definition file.
 100  
          */
 101  446
         private class MetaModelParser extends SAXHandler {
 102  
                 // XML element and attribute names of the metamodel definition
 103  
                 private static final String ELEM_MODELELEMENT = "modelelement";
 104  
                 private static final String ATTR_NAME = "name";
 105  
                 private static final String ATTR_PARENT = "parent";
 106  
                 private static final String ELEM_ATTRIBUTE = "attribute";
 107  
                 private static final String ATTR_TYPE = "type";
 108  
                 private static final String ATTR_MULTIPLICITY = "multiplicity";
 109  
 
 110  223
                 private MetaModelElement currentMME = null;
 111  223
                 private String currentAttributeName = null;
 112  
 
 113  
                 /** Clear the metamodel at the beginning of the XML document. */
 114  
                 @Override
 115  
                 public void startDocument() {
 116  223
                         elementMap.clear();
 117  223
                 }
 118  
 
 119  
                 /**
 120  
                  * Process an XML element in the metamodel definition file. Extracts and
 121  
                  * stores the metamodel element or attribute information.
 122  
                  * 
 123  
                  * @throws SAXException The XML file contains something other than model
 124  
                  *         element and attribute definitions.
 125  
                  */
 126  
                 @Override
 127  
                 public void startElement(String uri, String local, String raw,
 128  
                                 Attributes attrs) throws SAXException {
 129  5512
                         if (TL_ELEMENT.equals(raw)) {
 130  223
                                 checkVersion(attrs, null);
 131  222
                         } else if (ELEM_MODELELEMENT.equals(raw)) {
 132  2499
                                 handleMetaModelElement(attrs);
 133  2496
                         } else if (ELEM_ATTRIBUTE.equals(raw)) {
 134  2789
                                 handleAttributeDefinition(attrs);
 135  2785
                         } else {
 136  1
                                 reportError("Unexpected XML element <" + raw + ">.");
 137  
                         }
 138  5503
                 }
 139  
 
 140  
                 /**
 141  
                  * Handles a metamodel element definition.
 142  
                  * 
 143  
                  * @param attrs XML attributes of the element
 144  
                  * @throws SAXException metamodel element definition is invalid
 145  
                  */
 146  
                 private void handleMetaModelElement(Attributes attrs)
 147  
                                 throws SAXException {
 148  2499
                         String typeName = attrs.getValue(ATTR_NAME);
 149  2499
                         if (typeName == null) {
 150  1
                                 reportError("Model element is missing \"" + ATTR_NAME
 151  
                                                 + "\" attribute.");
 152  
                         }
 153  2498
                         String parentName = attrs.getValue(ATTR_PARENT);
 154  2498
                         if (parentName == null) {
 155  2139
                                 parentName = BASE_ELEMENT;
 156  
                         }
 157  2498
                         MetaModelElement parentElement = elementMap.get(parentName);
 158  2498
                         if (parentElement == null && !BASE_ELEMENT.equals(typeName)) {
 159  2
                                 if (BASE_ELEMENT.equals(parentName)) {
 160  1
                                         reportError("The first metamodel element to be defined must be named \""
 161  
                                                         + BASE_ELEMENT + "\".");
 162  
                                 }
 163  2
                                 reportError("Unknown parent type \"" + parentName
 164  1
                                                 + "\" for model element \"" + typeName + "\".");
 165  
                         }
 166  
 
 167  2496
                         currentMME = new MetaModelElement(typeName, parentElement);
 168  2496
                         elementMap.put(typeName, currentMME);
 169  2496
                         currentAttributeName = null;
 170  2496
                 }
 171  
 
 172  
                 /**
 173  
                  * Handles a metamodel attribute definition.
 174  
                  * 
 175  
                  * @param attrs XML attributes of the metamodel attribute
 176  
                  * @throws SAXException metamodel attribute definition is invalid
 177  
                  */
 178  
                 private void handleAttributeDefinition(Attributes attrs)
 179  
                                 throws SAXException {
 180  2789
                         if (currentMME == null) {
 181  1
                                 reportError("Attribute definition outside model element definition.");
 182  
                         }
 183  
 
 184  2788
                         currentAttributeName = attrs.getValue(ATTR_NAME);
 185  2788
                         if (currentAttributeName == null) {
 186  2
                                 reportError("Attribute without a name for model element \""
 187  1
                                                 + currentMME.getName() + "\".");
 188  
                         }
 189  
 
 190  2787
                         boolean isRefAttribute = "ref".equals(attrs.getValue(ATTR_TYPE));
 191  5574
                         boolean isSetAttribute = "many".equals(attrs
 192  2787
                                         .getValue(ATTR_MULTIPLICITY));
 193  
 
 194  2787
                         if ("extref".equals(attrs.getValue(ATTR_TYPE))) {
 195  103
                                 if (currentMME.getExtensionReference() != null) {
 196  2
                                         reportError("Duplicate extension reference attribute '"
 197  1
                                                         + currentAttributeName + "'.");
 198  
                                 }
 199  102
                                 if (isSetAttribute) {
 200  2
                                         reportError("Extension reference attribute '"
 201  1
                                                         + currentAttributeName
 202  1
                                                         + "' cannot be multi-valued.");
 203  
                                 }
 204  101
                                 isRefAttribute = true;
 205  101
                                 currentMME.setExtensionReference(currentAttributeName);
 206  
                         }
 207  
 
 208  5570
                         currentMME.addAttribute(currentAttributeName, isRefAttribute,
 209  2785
                                         isSetAttribute);
 210  2785
                 }
 211  
 
 212  
                 /** Registers end of metamodel element/attribute definitions. */
 213  
                 @Override
 214  
                 public void endElement(String uri, String local, String raw) {
 215  5491
                         if (ELEM_MODELELEMENT.equals(raw)) {
 216  2492
                                 currentMME = null;
 217  2492
                         } else if (ELEM_ATTRIBUTE.equals(raw)) {
 218  2785
                                 currentAttributeName = null;
 219  
                         }
 220  5491
                 }
 221  
 
 222  
                 /**
 223  
                  * Adds description text to the current attribute of the current element
 224  
                  * type.
 225  
                  */
 226  
                 @Override
 227  
                 public void characters(char[] ch, int start, int length) {
 228  9592
                         if (currentMME != null && currentAttributeName != null) {
 229  4634
                                 currentMME.addAttributeDescription(currentAttributeName,
 230  2317
                                                 new String(ch, start, length));
 231  
                         }
 232  9592
                 }
 233  
         }
 234  
 }