All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dellroad.stuff.jibx.MapEntry Maven / Gradle / Ivy

The newest version!

/*
 * Copyright (C) 2022 Archie L. Cobbs. All rights reserved.
 */

package org.dellroad.stuff.jibx;

import java.util.Iterator;
import java.util.Map;

import org.jibx.runtime.JiBXParseException;

/**
 * Utility class that makes it slightly easier to model {@link Map} properties in JiBX.
 * This class can be used to represent entries in the map, each of which is modeled in XML as a separate XML element.
 *
 * 
 * 
 * 
 * 
 *
 * 

* For example, suppose you have a class {@code Company} and want to add a {@code directory} property that has * type {@code Map}: *


 * public class Company {
 *     private Map<String, Person> directory = new HashMap<String, Person>();
 *
 *     // Getter and setter for the "directory" property
 *     public Map<String, Person> getDirectory() {
 *         return this.directory;
 *     }
 *     public void setDirectory(Map<String, Person> directory) {
 *         this.directory = directory;
 *     }
 * }
 * 
* *

* Because the JiBX binding process modifies class files, you first need to create your own subclass of {@link MapEntry} * that can be modified. In this example, we'll use an inner class of {@code Company}. In addition, you also need to add * JiBX "add-method" and "iter-method" helper methods. The resulting new code might look like this: *


 *     // JiBX holder for a single entry in the Directory map
 *     public static class DirectoryEntry extends MapEntry<String, Person> {
 *        public String getKey()   { return super.getKey();   }   // JiBX requires exact return types
 *        public Person getValue() { return super.getValue(); }   // JiBX requires exact return types
 *     }
 *
 *     // JiBX "add-method" that adds a new entry to the directory
 *     void addDirectoryEntry(DirectoryEntry entry) throws JiBXParseException {
 *         MapEntry.add(this.directory, entry);
 *     }
 *
 *     // JiBX "iter-method" that iterates all entries in the directory
 *     Iterator<DirectoryEntry> iterateDirectoryEntries() {
 *         return MapEntry.iterate(this.directory, DirectoryEntry.class);
 *     }
 * 
* *

* Then in your JiBX binding definition, you would do something like this: *


 * <binding package="com.example">
 *
 *     <!-- Include XML mapping definition for a Person object (having type-name "person") -->
 *     <include path="person.xml"/>
 *
 *     <!-- Define the XML mapping for one entry in the "directory" map -->
 *     <mapping abstract="true" type-name="directory_entry" class="com.example.Company$DirectoryEntry">
 *         <value name="name" get-method="getKey" set-method="setKey" type="java.lang.String" style="attribute"/>
 *         <structure name="Person" get-method="getValue" set-method="setValue" map-as="person"/>
 *     </mapping>
 *
 *     <!-- Define XML mapping for a Company object -->
 *     <mapping abstract="true" type-name="company" class="com.example.Company">
 *         <collection name="Directory" item-type="com.example.Company$DirectoryEntry"
 *           add-method="addDirectoryEntry" iter-method="iterateDirectoryEntries">
 *             <structure name="DirectoryEntry" map-as="directory_entry"/>
 *         </collection>
 *         <!-- other properties... -->
 *     </mapping>
 * </binding>
 * 
* * Then the resulting XML would end up looking something like this: *

 * <Company>
 *     <Directory>
 *         <DirectoryEntry name="George Washington">
 *             <Person>
 *                  <!-- properties of George Washington... -->
 *             </Person>
 *         </DirectoryEntry>
 *         <DirectoryEntry name="Betsy Ross">
 *             <Person>
 *                  <!-- properties of Betsy Ross... -->
 *             </Person>
 *         </DirectoryEntry>
 *     </Directory>
 *     <!-- other properties... -->
 * </Company>
 * 
* *

* Note that during unmarshalling, the Map itself is not created; it is expected to already exist * and be empty. This will be the case if you provide a field initializer as in the example above. * *

* The map keys are not constrained to being simple values: for complex keys, just adjust the mapping for the * {@code DirectoryEntry} structure accordingly. */ public class MapEntry { private K key; private V value; /** * Get this map entry's key. * * @return map entry key */ public K getKey() { return this.key; } public void setKey(K key) { this.key = key; } /** * Get this map entry's value. * * @return map entry value */ public V getValue() { return this.value; } public void setValue(V value) { this.value = value; } /** * Helper method intended to be used by a custom JiBX "iter-method". * This method returns an iterator that iterates over all entries in the given map. * * @param type of map keys * @param type of map values * @param map entry type * @param map map to iterate * @param entryClass the subclass of {@link MapEntry} used for iterated elements; must have a default constructor * @return map entry iterator */ public static > Iterator iterate(Map map, final Class entryClass) { final Iterator> entryIterator = map.entrySet().iterator(); return new Iterator() { @Override public boolean hasNext() { return entryIterator.hasNext(); } @Override public E next() { Map.Entry entry = entryIterator.next(); E mapEntry; try { mapEntry = entryClass.getDeclaredConstructor().newInstance(); } catch (ReflectiveOperationException e) { throw new RuntimeException("unexpected exception", e); } mapEntry.setKey(entry.getKey()); mapEntry.setValue(entry.getValue()); return mapEntry; } @Override public void remove() { entryIterator.remove(); } }; } /** * Helper method intended to be used by a custom JiBX "add-method". * If there is an existing entry with the same key, a {@link JiBXParseException} is thrown. * * @param type of map keys * @param type of map values * @param map map to which to add an new entry * @param entry new entry to add * @throws JiBXParseException if the map already contains an entry with the given key */ public static void add(Map map, MapEntry entry) throws JiBXParseException { MapEntry.add(map, entry, false); } /** * Helper method intended to be used by a custom JiBX "add-method". * * @param type of map keys * @param type of map values * @param map map to which to add an new entry * @param entry new entry to add * @param allowDuplicate {@code true} to replace any existing entry having the same key, * or {@code false} to throw a {@link JiBXParseException} if there is an existing entry * @throws JiBXParseException if {@code allowDuplicate} is {@code false} and an entry * with the same key already exists in {@code map} */ public static void add(Map map, MapEntry entry, boolean allowDuplicate) throws JiBXParseException { K key = entry.getKey(); V value = entry.getValue(); if (!allowDuplicate && map.containsKey(key)) throw new JiBXParseException("duplicate key in map", "" + key); map.put(key, value); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy