Coverage Report - net.sf.sapjcosupport.SapMappingParser
 
Classes in this File Line Coverage Branch Coverage Complexity
SapMappingParser
0% 
0% 
9,143
 
 1  
 package net.sf.sapjcosupport;
 2  
 
 3  
 import org.apache.log4j.Logger;
 4  
 import org.springframework.core.io.ClassPathResource;
 5  
 import org.springframework.util.xml.DomUtils;
 6  
 import org.w3c.dom.Document;
 7  
 import org.w3c.dom.Element;
 8  
 
 9  
 import javax.xml.parsers.DocumentBuilder;
 10  
 import javax.xml.parsers.DocumentBuilderFactory;
 11  
 import java.util.*;
 12  
 
 13  
 /**
 14  
  * This class parses a VO SAP mapping and caches it internally.
 15  
  *
 16  
  * @author Jo Vandermeeren
 17  
  * @since Jan 18, 2006 - 5:34:08 PM
 18  
  */
 19  0
 public class SapMappingParser {
 20  0
     private static final Logger logger = Logger.getLogger(SapMappingParser.class);
 21  
 
 22  
     /**
 23  
      * Internal caching class for <code>SapMapping</code> instances.
 24  
      */
 25  0
     private static class MappingCache {
 26  
         private static Map cache;
 27  
 
 28  
         static {
 29  0
             cache = Collections.synchronizedMap(new HashMap());
 30  0
         }
 31  
 
 32  
         public static synchronized SapMapping get(Class persistentClass) {
 33  0
             return (SapMapping) cache.get(persistentClass);
 34  
         }
 35  
 
 36  
         public static synchronized void register(Class persistentClass, SapMapping mapping) {
 37  0
             if (logger.isInfoEnabled()) {
 38  0
                 logger.info("Caching mapping for entity " + persistentClass.getName());
 39  
             }
 40  0
             cache.put(persistentClass, mapping);
 41  0
         }
 42  
     }
 43  
 
 44  
     /**
 45  
      * This method retrieves a cached mapping for a given value object class (VO).
 46  
      * If it doesn.t exist in cache, it will be looked up, parsed, cached and returned.
 47  
      *
 48  
      * @param clazz VO class
 49  
      * @return SAP mapping for the given class
 50  
      * @throws SapJcoMappingException if something goes wrong when creating the SAP mapping
 51  
      */
 52  
     public synchronized static SapMapping getInfo(Class clazz) throws SapJcoMappingException {
 53  0
         SapMapping mapping = MappingCache.get(clazz);
 54  0
         if (mapping == null) {
 55  0
             if (logger.isInfoEnabled()) {
 56  0
                 logger.info("Mapping for entity '" + clazz.getName() + "' not cached yet, parsing mapping definition");
 57  
             }
 58  0
             mapping = parseMapping(clazz);
 59  0
             MappingCache.register(clazz, mapping);
 60  
         }
 61  0
         return mapping;
 62  
     }
 63  
 
 64  
     /**
 65  
      * This helper method tries to find the SAP JCO mapping file in the same classpath location as the
 66  
      * <code>clazz</code> class definition. The mapping file has the same name as the class and ends with ".sap.xml".
 67  
      * This file is read and parsed to a <code>SapMapping</code> object.
 68  
      *
 69  
      * @param clazz the VO class
 70  
      * @return SAP mapping
 71  
      * @throws SapJcoMappingException if something goes wrong when creating the SAP mapping
 72  
      */
 73  
     private static SapMapping parseMapping(Class clazz) throws SapJcoMappingException {
 74  
         try {
 75  0
             String fqName = clazz.getName();
 76  0
             String resource = fqName.replace('.', '/');
 77  0
             String shortName = fqName.substring(fqName.lastIndexOf(".") + 1);
 78  0
             ClassPathResource classPathResource = new ClassPathResource(resource + ".sap.xml");
 79  
 
 80  0
             DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
 81  0
             Document mappingXml = builder.parse(classPathResource.getInputStream());
 82  
 
 83  0
             Element root = mappingXml.getDocumentElement();
 84  
 
 85  0
             List classDefs = DomUtils.getChildElementsByTagName(root, "class");
 86  0
             if (classDefs == null || classDefs.size() == 0) {
 87  0
                 throw new SapJcoMappingException(
 88  
                         "SAP Mapping xml '" + shortName + ".sap.xml' should contain 1 <class/> definition");
 89  
             }
 90  0
             Element classDef = (Element) classDefs.get(0);
 91  0
             String className = classDef.getAttribute("name");
 92  0
             String bapiName = classDef.getAttribute("bapi");
 93  0
             if (className == null || className.trim().length() == 0) {
 94  0
                 throw new SapJcoMappingException("'name' attribute of 'class' element is required");
 95  
             }
 96  
 
 97  0
             SapMapping sapMapping = new SapMapping(className);
 98  
 
 99  0
             boolean fieldSelectionExists = false;
 100  
             // Check if a lists elements is available and if available process the input and output list
 101  0
             List lists = DomUtils.getChildElementsByTagName(root, "list");
 102  0
             for (int i = 0; i < lists.size(); i++) {
 103  0
                 Element listsElement = (Element) lists.get(i);
 104  0
                 List inputLists = DomUtils.getChildElementsByTagName(listsElement, "inputList");
 105  0
                 for (Iterator inputListIterator = inputLists.iterator(); inputListIterator.hasNext();) {
 106  0
                     Element inputListElement = (Element) inputListIterator.next();
 107  0
                     String[] listAttributes = extractListElementAttributes(inputListElement);
 108  0
                     if (listAttributes != null) {
 109  0
                         sapMapping.addInputList(new SapListMapping(listAttributes[0], listAttributes[1]));
 110  
                     }
 111  
                 }
 112  0
                 String[] listAttributes = selectAndExtractListElementAttributes(listsElement, "outputList");
 113  0
                 if (listAttributes != null) {
 114  0
                     sapMapping.setOutputList(new SapListMapping(listAttributes[0], listAttributes[1]));
 115  
                 }
 116  0
                 listAttributes = selectAndExtractListElementAttributes(listsElement, "fieldSelection");
 117  0
                 if (listAttributes != null) {
 118  0
                     fieldSelectionExists = true;
 119  0
                     sapMapping.setFieldSelection(new SapListMapping(listAttributes[0], listAttributes[1]));
 120  
                 }
 121  
             }
 122  
 
 123  0
             Map fieldSelectionMap = new HashMap();
 124  0
             if (bapiName == null || bapiName.length() == 0) {
 125  0
                 throw new SapJcoMappingException("'bapi' attribute of 'class' element is required");
 126  
             }
 127  0
             SapBapiMapping bapi = new SapBapiMapping(bapiName);
 128  
 
 129  
             //loop over structures
 130  0
             List structureList = DomUtils.getChildElementsByTagName(classDef, "structure");
 131  0
             for (int j = 0; j < structureList.size(); j++) {
 132  0
                 Element structureElement = (Element) structureList.get(j);
 133  0
                 String structureName = structureElement.getAttribute("name");
 134  0
                 if (structureName == null || structureName.length() == 0) {
 135  0
                     throw new SapJcoMappingException("'name' attribute of 'structure' element is required");
 136  
                 }
 137  0
                 String mappingType = structureElement.getAttribute("type");
 138  
                 SapStructureMapping structure;
 139  
 
 140  0
                 if ("list".equalsIgnoreCase(mappingType)) {
 141  0
                     String keyField = structureElement.getAttribute("key-field");
 142  0
                     String primaryKey = structureElement.getAttribute("pk");
 143  0
                     if (keyField != null && keyField.trim().length() > 0 && primaryKey != null && primaryKey.trim()
 144  
                             .length() > 0) {
 145  
                         // List of Maps
 146  0
                         structure = new SapStructureMapping(structureName, SapStructureMapping.LIST_OF_MAPS);
 147  0
                         structure.setKeyField(keyField);
 148  0
                         structure.setPrimaryKey(primaryKey);
 149  
                     } else {
 150  
                         // List
 151  0
                         structure = new SapStructureMapping(structureName, SapStructureMapping.LIST);
 152  
                     }
 153  0
                 } else if ("map".equalsIgnoreCase(mappingType)) {
 154  0
                     String keyField = structureElement.getAttribute("key-field");
 155  0
                     if (keyField == null || keyField.length() == 0) {
 156  0
                         throw new SapJcoMappingException(
 157  
                                 "'key-field' attribute of 'structure' element is required when 'type' is 'map'");
 158  
                     }
 159  
                     // Map
 160  0
                     structure = new SapStructureMapping(structureName, SapStructureMapping.MAP);
 161  0
                     structure.setKeyField(keyField);
 162  0
                 } else if ("entity".equalsIgnoreCase(mappingType)) {
 163  0
                     String entityClass = structureElement.getAttribute("class");
 164  0
                     String collection = structureElement.getAttribute("collection");
 165  0
                     structure = new SapStructureMapping(structureName, SapStructureMapping.ENTITY);
 166  0
                     structure.setCollectionName(collection);
 167  0
                     structure.setEntityClass(entityClass);
 168  
                 } else {
 169  0
                     structure = new SapStructureMapping(structureName);
 170  
                 }
 171  
 
 172  
                 // This attribute defines if we're going to skip this structure when processing the output from SAP.
 173  
                 // Structures that do not contain return values, should usually be skipped when mapping value objects.
 174  0
                 String excluded = structureElement.getAttribute("excluded");
 175  0
                 if ("true".equalsIgnoreCase(excluded)) {
 176  0
                     structure.setExcluded(true);
 177  
                 }
 178  
 
 179  
                 //loop over properties
 180  0
                 List propertyList = DomUtils.getChildElementsByTagName(structureElement, "property");
 181  0
                 for (int k = 0; k < propertyList.size(); k++) {
 182  0
                     Element propertyElement = (Element) propertyList.get(k);
 183  0
                     String propertyName = propertyElement.getAttribute("name");
 184  0
                     String fieldName = propertyElement.getAttribute("field");
 185  0
                     String key = propertyElement.getAttribute("key");
 186  0
                     String type = propertyElement.getAttribute("type");
 187  0
                     String not_null = propertyElement.getAttribute("not-null");
 188  0
                     if (fieldSelectionExists) {
 189  0
                         String profiles = propertyElement.getAttribute("profiles");
 190  0
                         if (profiles != null) {
 191  0
                             StringTokenizer st = new StringTokenizer(profiles, ",", false);
 192  0
                             while (st.hasMoreTokens()) {
 193  0
                                 String profile = st.nextToken();
 194  0
                                 List selection = (List) fieldSelectionMap.get(profile);
 195  0
                                 if (selection == null) {
 196  0
                                     selection = new ArrayList();
 197  
                                 }
 198  0
                                 selection.add(fieldName);
 199  0
                                 fieldSelectionMap.put(profile, selection);
 200  
                             }
 201  
                         }
 202  
                     }
 203  0
                     if (not_null == null || not_null.length() == 0) {
 204  0
                         not_null = "false";
 205  
                     }
 206  0
                     if (!("true".equalsIgnoreCase(not_null) || "false".equalsIgnoreCase(not_null))) {
 207  0
                         throw new SapJcoMappingException(
 208  
                                 "'not-null' attribute of 'property' element should be 'false' or 'true'");
 209  
                     }
 210  0
                     if (propertyName == null || propertyName.length() == 0) {
 211  0
                         throw new SapJcoMappingException("'name' attribute of 'property' element is required");
 212  
                     }
 213  0
                     if (fieldName == null || fieldName.length() == 0) {
 214  0
                         throw new SapJcoMappingException("'field' attribute of 'property' element is required");
 215  
                     }
 216  0
                     if (type == null || type.length() == 0) {
 217  0
                         type = "string";
 218  
                     }
 219  0
                     boolean required = Boolean.valueOf(not_null).booleanValue();
 220  0
                     if (structure.isMap() || structure.isListOfMaps()) {
 221  0
                         if (key == null || key.length() == 0) {
 222  0
                             throw new SapJcoMappingException(
 223  
                                     "'key' attribute of 'property' element is required when the structure is of type 'map'");
 224  
                         }
 225  0
                         structure.addField(new SapFieldMapping(propertyName, fieldName, type, key, className,
 226  
                                                                required));
 227  
                     } else {
 228  0
                         structure.addField(new SapFieldMapping(propertyName, fieldName, type, className, required));
 229  
                     }
 230  
                 }
 231  0
                 bapi.addStructure(structure);
 232  
             }
 233  
             //read input settings: <input name="PLANT" not-null="false" default="NC01"/>
 234  0
             List inputList = DomUtils.getChildElementsByTagName(classDef, "input");
 235  0
             for (int j = 0; j < inputList.size(); j++) {
 236  0
                 Element inputElement = (Element) inputList.get(j);
 237  0
                 String inputName = inputElement.getAttribute("name");
 238  0
                 String inputDefault = inputElement.getAttribute("default");
 239  0
                 String notNull = inputElement.getAttribute("not-null");
 240  0
                 String pk = inputElement.getAttribute("pk");
 241  0
                 if (inputName == null || inputName.length() == 0) {
 242  0
                     throw new SapJcoMappingException("'name' attribute of 'input' element is required");
 243  
                 }
 244  0
                 boolean required = toBoolean(notNull, "false");
 245  0
                 boolean isPk = toBoolean(pk, "false");
 246  0
                 bapi.addInput(new SapInput(inputName, inputDefault, required, isPk));
 247  
             }
 248  0
             bapi.setFieldSelection(fieldSelectionMap);
 249  0
             sapMapping.setBapi(bapi);
 250  
 
 251  0
             return sapMapping;
 252  0
         } catch (Exception e) {
 253  0
             String msg = new StringBuffer("Error loading mapping info for entity '").append(clazz.getName()).append("'")
 254  
                     .toString();
 255  0
             logger.error(msg, e);
 256  0
             throw new SapJcoMappingException(msg);
 257  
         }
 258  
     }
 259  
 
 260  
     /**
 261  
      * This helper method extracts the first child element with name <code>childElement</code> from
 262  
      * the XML element <code>parentListsElement</code> and extracts the String values that are needed to construct
 263  
      * the input resp. output list.
 264  
      *
 265  
      * @param parentListsElement parent XML element
 266  
      * @param childElement       child XML element name
 267  
      * @return String array containing <code>name</code> attribute (at position 0) and <code>pk</code> attribute (at position 1)
 268  
      * @throws SapJcoMappingException if the 'name' or 'pk' attribute is not available in the XML element
 269  
      */
 270  
     private static String[] selectAndExtractListElementAttributes(Element parentListsElement,
 271  
                                                                   String childElement) throws SapJcoMappingException {
 272  0
         List list = DomUtils.getChildElementsByTagName(parentListsElement, childElement);
 273  0
         if (list != null && list.size() > 0) {
 274  0
             return extractListElementAttributes((Element) list.get(0));
 275  
         }
 276  0
         return null;
 277  
     }
 278  
 
 279  
     private static String[] extractListElementAttributes(Element element) {
 280  0
         if (element == null) {
 281  0
             return null;
 282  
         }
 283  0
         String[] attributes = new String[2];
 284  0
         attributes[0] = element.getAttribute("name");
 285  0
         if (attributes[0] == null || attributes[0].trim().length() == 0) {
 286  0
             throw new SapJcoMappingException("'name' attribute of '" + element + "' element in 'list' is required");
 287  
         }
 288  0
         attributes[1] = element.getAttribute("pk");
 289  0
         if (attributes[1] == null || attributes[1].trim().length() == 0) {
 290  0
             throw new SapJcoMappingException("'pk' attribute of '" + element + "' element in 'list' is required");
 291  
         }
 292  0
         return attributes;
 293  
     }
 294  
 
 295  
     /**
 296  
      * Converts String "true" to boolean <code>true</code> and String "false" to boolean <code>false</code>.
 297  
      *
 298  
      * @param booleanValue String value that should be parsed to boolean
 299  
      * @param defaultValue default value to use in case booleanValue is null or zero in length
 300  
      * @return boolean that corresponds with the String parameter
 301  
      */
 302  
     private static boolean toBoolean(String booleanValue, String defaultValue) {
 303  0
         if (booleanValue == null || booleanValue.length() == 0) {
 304  0
             booleanValue = defaultValue;
 305  
         }
 306  0
         if (!("true".equalsIgnoreCase(booleanValue) || "false".equalsIgnoreCase(booleanValue))) {
 307  0
             throw new SapJcoMappingException("boolean attributes should be 'false' or 'true'");
 308  
         }
 309  0
         return Boolean.valueOf(booleanValue).booleanValue();
 310  
     }
 311  
 }