// // Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s). // All rights reserved. // package openadk.library.tools.mapping; import org.w3c.dom.*; import java.util.*; /** * An ObjectMapping defines a set of field mapping rules for a specific SIF Data * Object type such as StudentPersonal, StaffPersonal, or BusInfo. ObjectMapping * is comprised of zero or more FieldMapping children.<p> * * @author Eric Petersen * @version ADK 1.0 */ public class ObjectMapping { /** * The SIF Data Object type (e.g. StudentPersonal) */ protected String fObjType; /** * Optional DOM Node from which this ObjectType was produced */ protected Node fNode; /** * Vector of FieldMappings */ // protected List<FieldMapping> fFieldRules = null; JEN protected List<Mapping> fFieldRules = null; /** * Map of prefix to namespace URI for this object * Using string for URI type as it matches the XML DOM API */ protected Map<String, String> prefixToNamespace = new HashMap<String, String>(); /** * The parent Mappings object */ protected Mappings fParent; /** * Constructor * @param objType The name of a SIF Data Object (e.g. "StudentPersonal") */ public ObjectMapping( String objType ) throws ADKMappingException { this( objType, null ); } /** * Constructor * @param objType The name of a SIF Data Object (e.g. "StudentPersonal") * @param node The optional DOM Node from which this ObjectType was produced */ public ObjectMapping( String objType, Node node ) throws ADKMappingException { fObjType = objType; fNode = node; } /** * Gets the optional DOM Node associated with this ObjectMapping instance. * The DOM Node is usually set by the parent Mappings object when an * ObjectMapping is populated from a DOM Document. */ public Node getNode() { return fNode; } /** * Sets the optional DOM Node associated with this ObjectMapping instance. * The DOM Node is usually set by the parent Mappings object when an * ObjectMapping is populated from a DOM Document. */ public void setNode( Node node ) { fNode = node; } /** * Creates a copy this ObjectMapping instance.<p> * * This method performs a "deep copy", such that a clone is made of each * child FieldMapping. The parent of the new ObjectMapping will be the * Mappings object passed to this function. Any DOM Nodes assigned to this * object or its children are cloned and appended to the parent Mappings's * DOM Node if one exists.<p> * * @return A "deep copy" of this object */ public ObjectMapping copy( Mappings newParent ) throws ADKMappingException { // Create a new ObjectMapping instance ObjectMapping m = new ObjectMapping( fObjType ); // Copy the DOM Node if( fNode != null && newParent.fNode != null ) { m.fNode = newParent.fNode.getOwnerDocument().importNode( fNode, false ); if( newParent.fNode != null ) newParent.fNode.appendChild( m.fNode ); } // Copy fFieldRules if( fFieldRules != null ) { if( m.fFieldRules == null ) // m.fFieldRules = new Vector<FieldMapping>(); JEN m.fFieldRules = new Vector<Mapping>(); for( int i = 0; i < fFieldRules.size(); i++ ) { // FieldMapping copy = (fFieldRules.get(i)).copy( m ); JEN Mapping copy = (fFieldRules.get(i)).copy( m ); // m.addRule( copy ); JEN if (copy instanceof FieldMapping) m.addRule( (FieldMapping)copy ); } } return m; } /** * Gets the SIF Data Object type of this ObjectMapping * @return An object type name such as "StudentPersonal" or "BusInfo" */ public String getObjectType() { return fObjType; } /** * Appends a FieldMapping definition<p> * * @param mapping A FieldMapping that defines the rules for mapping a field * of the application to an element or attribute of a SIF Data Object. * There can only be one FieldMapping per unique field name (i.e. if * you have defined a FieldMapping rule with a field name of 'STUDENTNUM', * there cannot be another FieldMapping rule with that same field name.) * To map a single application field to more than one SIF element or * attribute, create a FieldMapping with a unique field name (e.g. 'STUDENTNUM_2') * and call the <code>setAlias</code> method to define it as an alias * of an existing field. * * @exception ADKMappingException thrown if there is already a FieldMapping * with the specified field name */ public void addRule( FieldMapping mapping ) throws ADKMappingException { insertRule( mapping, fFieldRules == null ? 0 : fFieldRules.size(), true ); } /** * Appends a FieldMapping definition<p> * * @param mapping A FieldMapping that defines the rules for mapping a field * of the application to an element or attribute of a SIF Data Object. * There can only be one FieldMapping per unique field name (i.e. if * you have defined a FieldMapping rule with a field name of 'STUDENTNUM', * there cannot be another FieldMapping rule with that same field name.) * To map a single application field to more than one SIF element or * attribute, create a FieldMapping with a unique field name (e.g. 'STUDENTNUM_2') * and call the <code>setAlias</code> method to define it as an alias * of an existing field. * * @param buildDomTree true to create a DOM Node element for this * FieldMapping and append it to the parent Node * * @exception ADKMappingException thrown if there is already a FieldMapping * with the specified field name */ protected void addRule( FieldMapping mapping, boolean buildDomTree ) throws ADKMappingException { insertRule( mapping, fFieldRules == null ? 0 : fFieldRules.size(), buildDomTree ); } /** * Insert a FieldMapping definition at the specified index.<p> * * @param mapping A FieldMapping that defines the rules for mapping a field * of the application to an element or attribute of a SIF Data Object. * There can only be one FieldMapping per unique field name (i.e. if * you have defined a FieldMapping rule with a field name of 'STUDENTNUM', * there cannot be another FieldMapping rule with that same field name.) * To map a single application field to more than one SIF element or * attribute, create a FieldMapping with a unique field name (e.g. 'STUDENTNUM_2') * and call the <code>setAlias</code> method to define it as an alias * of an existing field. * * @exception ADKMappingException thrown if there is already a FieldMapping * with the specified field name */ public void insertRule( FieldMapping mapping, int index ) throws ADKMappingException { insertRule( mapping, index, true ); } /** * Insert a FieldMapping definition at the specified index.<p> * * @param mapping A FieldMapping that defines the rules for mapping a field * of the application to an element or attribute of a SIF Data Object. * There can only be one FieldMapping per unique field name (i.e. if * you have defined a FieldMapping rule with a field name of 'STUDENTNUM', * there cannot be another FieldMapping rule with that same field name.) * To map a single application field to more than one SIF element or * attribute, create a FieldMapping with a unique field name (e.g. 'STUDENTNUM_2') * and call the <code>setAlias</code> method to define it as an alias * of an existing field. * * @param buildDomTree true to create a DOM Node element for this * FieldMapping and append it to the parent Node * * @exception ADKMappingException thrown if there is already a FieldMapping * with the specified field name */ protected void insertRule( FieldMapping mapping, int index, boolean buildDomTree ) throws ADKMappingException { Node relativeTo = null; if( fFieldRules == null ) // fFieldRules = new Vector<FieldMapping>(); JEN fFieldRules = new Vector<Mapping>(); else { // Check for duplicate for( int i = 0; i < fFieldRules.size(); i++ ) { if( (fFieldRules.get(i)).getKey().equals( mapping.getKey() ) ) throw new ADKMappingException( "Duplicate field mapping: " + mapping.getFieldName(), null ); } // If we'll be building a child DOM Node, find the existing Node // that it will be inserted at if( buildDomTree && fNode != null && fFieldRules.size() > index ) relativeTo = (fFieldRules.get( index )).getNode(); } try { fFieldRules.add( index, mapping ); } catch( Exception ex ) { throw new ADKMappingException( ex.toString(), null, ex ); } if( buildDomTree && fNode != null ) { // Create and insert a child DOM Node Element element = fNode.getOwnerDocument().createElement( Mappings.XML_FIELD ); mapping.setNode( element ); mapping.toXML( element ); if( relativeTo != null ) fNode.insertBefore( element , relativeTo ); else fNode.appendChild( element ); } } /** * Remove a FieldMapping definition */ public void removeRule( FieldMapping mapping ) { if( fFieldRules != null ) { fFieldRules.remove( mapping ); // Remove the DOM Node if there is one Node n = mapping.getNode(); if( n != null ) n.getParentNode().removeChild( n ); } } /** * Removes the FieldMapping at the specified index * @param index The zero-based index of the FieldMapping */ public void removeRule( int index ) { if( fFieldRules != null && index >= 0 && index < fFieldRules.size() ) { // FieldMapping existing = fFieldRules.get( index ); JEN Mapping existing = fFieldRules.get( index ); fFieldRules.remove( index ); // Remove the DOM Node if there is one Node n = existing == null ? null : existing.getNode(); if( n != null ) n.getParentNode().removeChild( n ); } } /** * Return an array of all FieldMapping definitions * @param inherit True to inherit FieldMapping definitions from the * parent Mappings ancestry * @return A list of all FieldMapping definitions * @deprecated Please change all references to use getAllRulesList(); */ public FieldMapping[] getRules( boolean inherit ) { List<FieldMapping> rules = getRulesList( inherit ); FieldMapping[] arr = new FieldMapping[ rules.size() ]; rules.toArray( arr ); return arr; } /** * Return a list of all FieldMapping definitions * @param inherit True to inherit FieldMapping definitions from the * parent Mappings ancestry * @return A list of all FieldMapping definitions * @deprecated Please use getAllRulesList */ public List<FieldMapping> getRulesList( boolean inherit ) { List<FieldMapping> rules = new ArrayList<FieldMapping>(); if( inherit ) { Set<String> set = new HashSet<String>(); // Keep the rules in a list because the ordering of // rules is important Mappings m = fParent; while( m != null ) { ObjectMapping om = m.getRules( fObjType, false ); if( om != null && om.fFieldRules != null ) { // for( FieldMapping fm : om.fFieldRules ) { JEN for( Mapping fm : om.fFieldRules ) { String key = fm.getKey(); if( !set.contains( key ) ) { set.add( key ); if (fm instanceof FieldMapping) { rules.add((FieldMapping)fm ); } } } } if( inherit ) m = m.fParent; else m = null; } } else if( fFieldRules != null ) { // rules.addAll( fFieldRules ); for( Mapping mapping : fFieldRules ) { if (mapping instanceof FieldMapping) { rules.add((FieldMapping)mapping ); } } } return rules; } /** * Return a list of all FieldMapping definitions * @param inherit True to inherit FieldMapping definitions from the * parent Mappings ancestry * @return A list of all FieldMapping definitions */ public List<Mapping> getAllRulesList( boolean inherit ) { List<Mapping> rules = new ArrayList<Mapping>(); if( inherit ) { Set<String> set = new HashSet<String>(); // Keep the rules in a list because the ordering of // rules is important Mappings m = fParent; while( m != null ) { ObjectMapping om = m.getRules( fObjType, false ); if( om != null && om.fFieldRules != null ) { // for( FieldMapping fm : om.fFieldRules ) { JEN for( Mapping fm : om.fFieldRules ) { String key = fm.getKey(); if( !set.contains( key ) ){ set.add( key ); rules.add( fm ); } } } if( inherit ) m = m.fParent; else m = null; } } else if( fFieldRules != null ) { rules.addAll( fFieldRules ); } return rules; } /** * Count the number of FieldMapping definitions. */ public int getRuleCount() { return fFieldRules == null ? 0 : fFieldRules.size(); } /** * Gets the FieldMapping at the specified index * @param index The zero-based index of the FieldMapping */ public FieldMapping getRule( int index ) { return fFieldRules == null || index >= fFieldRules.size() ? null : (FieldMapping)fFieldRules.get( index ); } /** * Clear all FieldMapping definitions. */ public void clearRules() { if( fFieldRules != null ) { if( fNode != null ) { for( Iterator<Mapping> it = fFieldRules.iterator(); it.hasNext(); ) { Mapping m = it.next(); Node n = m.getNode(); if( n != null ) n.getParentNode().removeChild( n ); } } fFieldRules.clear(); } } public void addListingMapping(ListMapping listMapping) { if( fFieldRules == null ) { fFieldRules = new Vector<Mapping>(); } fFieldRules.add(listMapping); } /** * Return a map of all the namespace prefix mappings for this object mapping * @return a map of all the namespace prefix mappings for this object mapping */ public Map<String, String> getAllNamespaceURIs() { return getAllNamespaceURIs(false); } /** * Returns all known xml namespace declarations as prefix mapped to URI * The inheritence feature of this method is different than regular mappings inheritence. It * actually causes the associated XML DOM tree, if any exists, to be searched for other namespace * prefixes declared in the scopes containing this object mapping, and may search outside the mappings * definitions themselves if the associated DOM document exceeds them. * @param inheritXmlNS if true, the accessible XML tree will be traversed upwards and all declared namespaces will be included * @return all known prefix to namespace URI mappings */ public Map<String, String> getAllNamespaceURIs(boolean inheritXmlNS) { Map<String, String> fullMap = prefixToNamespace; if (inheritXmlNS && fNode != null) { fullMap = new HashMap<String, String>(prefixToNamespace); Node node = fNode.getParentNode(); while(node != null) { if (node.hasAttributes()) { NamedNodeMap nnm = node.getAttributes(); for (int nnmIdx = 0; nnmIdx < nnm.getLength(); nnmIdx++) { Node item = nnm.item(nnmIdx); if (item instanceof Attr) { Attr attr = (Attr)item; if (attr.getNodeName().startsWith("xmlns:")) { String foundPrefix = attr.getNodeName().substring(6); if (!fullMap.containsKey(foundPrefix)) { fullMap.put(foundPrefix, attr.getNodeValue()); } } } } } node = node.getParentNode(); } } return Collections.unmodifiableMap(fullMap); } public String getNamespaceURIForPrefix(String prefix) { return prefixToNamespace.get(prefix); } public void setNamespaceURIForPrefix(String prefix, String namespaceURI) { setNamespaceURIForPrefix(prefix, namespaceURI, true); } protected void setNamespaceURIForPrefix(String prefix, String namespaceURI, boolean buildDomTree) { if (prefix == null || namespaceURI == null) throw new IllegalArgumentException("This method does not accept null parameters"); prefixToNamespace.put(prefix, namespaceURI); if (fNode != null && fNode instanceof Element && buildDomTree) { Element object = (Element)fNode; object.setAttribute("xmlns:" + prefix, namespaceURI); } } public void removePrefix(String prefix) { prefixToNamespace.remove(prefix); if (fNode != null && fNode instanceof Element) { ((Element)fNode).removeAttribute("xmlns:" + prefix); } } }