001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021 022package org.biojava.nbio.ontology.utils; 023 024import java.io.Serializable; 025import java.util.*; 026 027 028/** 029 * A utility class to ease the problem of implementing an Annotation to that of 030 * providing an apropreate implementation of Map. Where possible implementations 031 * 032 * This class is only intended as a way to implement 033 * Annotation. If you are not trying to do that, then don't read on. If you 034 * are reading the documentation for an Annotation implementation that extends 035 * this, then don't read on. There is nothing to see here. 036 * 037 * If you are still reading this, then you must be trying to 038 * implement Annotation. To do that, extend this class and implement 039 * <code>getProperties()</code> and <code>propertiesAllocated()</code>. 040 * Where possible implementations should be backed with a 041 * <code>LinkedHashMap</code> or similar so properties are iterated in the order 042 * they were added. 043 * 044 * @author Matthew Pocock 045 * @author Greg Cox 046 * 047 * @since 1.0 048 */ 049public abstract class AbstractAnnotation 050 051 implements 052 Annotation, 053 Serializable 054{ 055 /** 056 * 057 */ 058 private static final long serialVersionUID = 2753449055959952873L; 059 060/** 061 * Implement this to return the Map delegate. Modifying this return value will 062 * modify the properties associated with this annotation. 063 * 064 * From code in the 1.2 version of AbstractAnnotation 065 * This is required for the implementation of an Annotation that 066 * extends AbstractAnnotation. Where possible implementations 067 * should be backed with a 068 * <code>LinkedHashMap</code> or similar so properties are iterated in the order 069 * they were added. 070 * 071 * @return a Map containing all properties 072 */ 073 protected abstract Map getProperties(); 074 075 /** 076 * A convenience method to see if we have allocated the properties 077 * Map. 078 * This is required for the implementation of an Annotation that 079 * extends AbstractAnnotation. 080 * @return true if the properties have been allocated, false otherwise 081 * 082 */ 083 protected abstract boolean propertiesAllocated(); 084 085 086 @Override 087 public Object getProperty(Object key) throws NoSuchElementException { 088 if(propertiesAllocated()) { 089 Map prop = getProperties(); 090 if(prop.containsKey(key)) { 091 return prop.get(key); 092 } 093 } 094 throw new NoSuchElementException("Property " + key + " unknown"); 095 } 096 097 @Override 098 public void setProperty(Object key, Object value) 099 { 100 101 getProperties().put(key, value); 102 103 } 104 105 @Override 106 public void removeProperty(Object key) 107 throws NoSuchElementException 108 { 109 if (!getProperties().containsKey(key)) { 110 throw new NoSuchElementException("Can't remove key " + key.toString()); 111 } 112 113 114 getProperties().remove(key); 115 116 } 117 118 @Override 119 public boolean containsProperty(Object key) { 120 if(propertiesAllocated()) { 121 return getProperties().containsKey(key); 122 } else { 123 return false; 124 } 125 } 126 127 @Override 128 public Set keys() { 129 if(propertiesAllocated()) { 130 return getProperties().keySet(); 131 } else { 132 return Collections.EMPTY_SET; 133 } 134 } 135 136 @Override 137 public String toString() { 138 StringBuffer sb = new StringBuffer("{"); 139 Map prop = getProperties(); 140 Iterator i = prop.keySet().iterator(); 141 if(i.hasNext()) { 142 Object key = i.next(); 143 sb.append(key).append("=").append(prop.get(key)); 144 } 145 while(i.hasNext()) { 146 Object key = i.next(); 147 sb.append(",").append(key).append("=").append(prop.get(key)); 148 } 149 sb.append("}"); 150 return sb.substring(0); 151 } 152 153 @Override 154 public Map asMap() { 155 return Collections.unmodifiableMap(getProperties()); 156 } 157 158 /** 159 * Protected no-args constructor intended for sub-classes. This class is 160 * abstract and can not be directly instantiated. 161 */ 162 protected AbstractAnnotation() { 163 } 164 165 /** 166 * Copy-constructor. 167 * 168 * <p> 169 * This does a shallow copy of the annotation. The result is an annotation 170 * with the same properties and values, but which is independant of the 171 * original annotation. 172 * </p> 173 * 174 * @param ann the Annotation to copy 175 */ 176 protected AbstractAnnotation(Annotation ann) { 177 if(ann == null) { 178 throw new NullPointerException( 179 "Null annotation not allowed. Use Annotation.EMPTY_ANNOTATION instead." 180 ); 181 } 182 if(ann == Annotation.EMPTY_ANNOTATION) { 183 return; 184 } 185 Map properties = getProperties(); 186 for(Iterator i = ann.keys().iterator(); i.hasNext(); ) { 187 Object key = i.next(); 188 try { 189 properties.put(key, ann.getProperty(key)); 190 } catch (IllegalArgumentException iae) { 191 throw new RuntimeException( 192 "Property was there and then disappeared: " + key, iae 193 ); 194 } 195 } 196 } 197 198 /** 199 * Create a new Annotation by copying the key-value pairs from a map. The 200 * resulting Annotation is independant of the map. 201 * 202 * @param annMap the Map to copy from. 203 */ 204 public AbstractAnnotation(Map annMap) { 205 if(annMap == null) { 206 throw new IllegalArgumentException( 207 "Null annotation Map not allowed. Use an empy map instead." 208 ); 209 } 210 if(annMap.isEmpty()) { 211 return; 212 } 213 214 Map properties = getProperties(); 215 for(Iterator i = annMap.keySet().iterator(); i.hasNext(); ) { 216 Object key = i.next(); 217 properties.put(key, annMap.get(key)); 218 } 219 } 220 221 222 @Override 223 public int hashCode() { 224 return asMap().hashCode(); 225 } 226 227 @Override 228 public boolean equals(Object o) { 229 if(o == this){ 230 return true; 231 } 232 if (! (o instanceof Annotation)) { 233 return false; 234 } 235 236 return ((Annotation) o).asMap().equals(asMap()); 237 } 238}