/hudson-core/src/main/java/hudson/util/DescribableList.java

http://github.com/hudson/hudson · Java · 257 lines · 134 code · 27 blank · 96 comment · 9 complexity · b88e385eeede47622a0d1ce6852b9424 MD5 · raw file

  1. /*
  2. * The MIT License
  3. *
  4. * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. package hudson.util;
  25. import com.thoughtworks.xstream.converters.Converter;
  26. import com.thoughtworks.xstream.converters.MarshallingContext;
  27. import com.thoughtworks.xstream.converters.UnmarshallingContext;
  28. import com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter;
  29. import com.thoughtworks.xstream.io.HierarchicalStreamReader;
  30. import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
  31. import com.thoughtworks.xstream.mapper.Mapper;
  32. import hudson.model.AbstractProject;
  33. import hudson.model.DependecyDeclarer;
  34. import hudson.model.DependencyGraph;
  35. import hudson.model.Describable;
  36. import hudson.model.Descriptor;
  37. import hudson.model.Descriptor.FormException;
  38. import hudson.model.Saveable;
  39. import net.sf.json.JSONObject;
  40. import org.kohsuke.stapler.StaplerRequest;
  41. import java.io.IOException;
  42. import java.util.ArrayList;
  43. import java.util.Collection;
  44. import java.util.List;
  45. import java.util.Map;
  46. /**
  47. * Persisted list of {@link Describable}s with some operations specific
  48. * to {@link Descriptor}s.
  49. *
  50. * <p>
  51. * This class allows multiple instances of the same descriptor. Some clients
  52. * use this semantics, while other clients use it as "up to one instance per
  53. * one descriptor" model.
  54. *
  55. * Some of the methods defined in this class only makes sense in the latter model,
  56. * such as {@link #remove(Descriptor)}.
  57. *
  58. * @author Kohsuke Kawaguchi
  59. */
  60. public class DescribableList<T extends Describable<T>, D extends Descriptor<T>> extends PersistedList<T> {
  61. protected DescribableList() {
  62. }
  63. /**
  64. * @deprecated since 2008-08-15.
  65. * Use {@link #DescribableList(Saveable)}
  66. */
  67. public DescribableList(Owner owner) {
  68. setOwner(owner);
  69. }
  70. public DescribableList(Saveable owner) {
  71. setOwner(owner);
  72. }
  73. public DescribableList(Saveable owner, Collection<? extends T> initialList) {
  74. super(initialList);
  75. setOwner(owner);
  76. }
  77. /**
  78. * @deprecated since 2008-08-15.
  79. * Use {@link #setOwner(Saveable)}
  80. */
  81. public void setOwner(Owner owner) {
  82. this.owner = owner;
  83. }
  84. /**
  85. * Removes all instances of the same type, then add the new one.
  86. */
  87. public void replace(T item) throws IOException {
  88. removeAll((Class)item.getClass());
  89. data.add(item);
  90. onModified();
  91. }
  92. public T get(D descriptor) {
  93. for (T t : data)
  94. if(t.getDescriptor()==descriptor)
  95. return t;
  96. return null;
  97. }
  98. public boolean contains(D d) {
  99. return get(d)!=null;
  100. }
  101. public void remove(D descriptor) throws IOException {
  102. for (T t : data) {
  103. if(t.getDescriptor()==descriptor) {
  104. data.remove(t);
  105. onModified();
  106. return;
  107. }
  108. }
  109. }
  110. /**
  111. * Creates a detached map from the current snapshot of the data, keyed from a descriptor to an instance.
  112. */
  113. @SuppressWarnings("unchecked")
  114. public Map<D,T> toMap() {
  115. return (Map)Descriptor.toMap(data);
  116. }
  117. /**
  118. * Rebuilds the list by creating a fresh instances from the submitted form.
  119. * <p/>
  120. * <p/>
  121. * This method is almost always used by the owner.
  122. * This method does not invoke the save method.
  123. *
  124. * @param json Structured form data that includes the data for nested descriptor list.
  125. * @deprecated as of 2.2.0,
  126. * use {@link DescribableListUtil#buildFromJson(hudson.model.Saveable, org.kohsuke.stapler.StaplerRequest, net.sf.json.JSONObject, java.util.List)}
  127. */
  128. public void rebuild(StaplerRequest req, JSONObject json, List<? extends Descriptor<T>> descriptors)
  129. throws FormException, IOException {
  130. List<T> newList = new ArrayList<T>();
  131. for (Descriptor<T> d : descriptors) {
  132. String name = d.getJsonSafeClassName();
  133. if (json.has(name)) {
  134. T instance = d.newInstance(req, json.getJSONObject(name));
  135. newList.add(instance);
  136. }
  137. }
  138. replaceBy(newList);
  139. }
  140. /**
  141. * @deprecated as of 1.271
  142. * Use {@link #rebuild(StaplerRequest, JSONObject, List)} instead.
  143. */
  144. public void rebuild(StaplerRequest req, JSONObject json, List<? extends Descriptor<T>> descriptors, String prefix) throws FormException, IOException {
  145. rebuild(req,json,descriptors);
  146. }
  147. /**
  148. * Rebuilds the list by creating a fresh instances from the submitted form.
  149. * <p/>
  150. * This version works with the the &lt;f:hetero-list> UI tag, where the user
  151. * is allowed to create multiple instances of the same descriptor. Order is also
  152. * significant.
  153. *
  154. * @deprecated as of 2.2.0,
  155. * use {@link DescribableListUtil#buildFromHetero(hudson.model.Saveable, org.kohsuke.stapler.StaplerRequest, net.sf.json.JSONObject, String, java.util.Collection)}
  156. * or {@link Descriptor#newInstancesFromHeteroList(org.kohsuke.stapler.StaplerRequest, net.sf.json.JSONObject, String, java.util.Collection)}
  157. */
  158. public void rebuildHetero(StaplerRequest req,
  159. JSONObject formData,
  160. Collection<? extends Descriptor<T>> descriptors,
  161. String key)
  162. throws FormException, IOException {
  163. replaceBy(Descriptor.newInstancesFromHeteroList(req, formData, key, descriptors));
  164. }
  165. /**
  166. * Picks up {@link DependecyDeclarer}s and allow it to build dependencies.
  167. */
  168. public void buildDependencyGraph(AbstractProject owner,DependencyGraph graph) {
  169. for (Object o : this) {
  170. if (o instanceof DependecyDeclarer) {
  171. DependecyDeclarer dd = (DependecyDeclarer) o;
  172. dd.buildDependencyGraph(owner,graph);
  173. }
  174. }
  175. }
  176. /*
  177. The following two seemingly pointless method definitions are necessary to produce
  178. backward compatible binary signatures. Without this we only get
  179. get(Ljava/lang/Class;)Ljava/lang/Object; from PersistedList where we need
  180. get(Ljava/lang/Class;)Lhudson/model/Describable;
  181. */
  182. public <U extends T> U get(Class<U> type) {
  183. return super.get(type);
  184. }
  185. public T[] toArray(T[] array) {
  186. return super.toArray(array);
  187. }
  188. /**
  189. * @deprecated since 2008-08-15.
  190. * Just implement {@link Saveable}.
  191. */
  192. public interface Owner extends Saveable {
  193. }
  194. /**
  195. * {@link Converter} implementation for XStream.
  196. *
  197. * Serializaion form is compatible with plain {@link List}.
  198. */
  199. public static class ConverterImpl extends AbstractCollectionConverter {
  200. CopyOnWriteList.ConverterImpl copyOnWriteListConverter;
  201. public ConverterImpl(Mapper mapper) {
  202. super(mapper);
  203. copyOnWriteListConverter = new CopyOnWriteList.ConverterImpl(mapper());
  204. }
  205. public boolean canConvert(Class type) {
  206. // handle subtypes in case the onModified method is overridden.
  207. return DescribableList.class.isAssignableFrom(type);
  208. }
  209. public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
  210. for (Object o : (DescribableList) source)
  211. writeItem(o, context, writer);
  212. }
  213. public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
  214. CopyOnWriteList core = copyOnWriteListConverter.unmarshal(reader, context);
  215. try {
  216. DescribableList r = (DescribableList)context.getRequiredType().newInstance();
  217. r.data.replaceBy(core);
  218. return r;
  219. } catch (InstantiationException e) {
  220. InstantiationError x = new InstantiationError();
  221. x.initCause(e);
  222. throw x;
  223. } catch (IllegalAccessException e) {
  224. IllegalAccessError x = new IllegalAccessError();
  225. x.initCause(e);
  226. throw x;
  227. }
  228. }
  229. }
  230. }