001: /*
002: * @(#)AutoDoc.java
003: *
004: * Copyright (C) 2002-2003 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Part of the GroboUtils package at:
009: * http://groboutils.sourceforge.net
010: *
011: * Permission is hereby granted, free of charge, to any person obtaining a
012: * copy of this software and associated documentation files (the "Software"),
013: * to deal in the Software without restriction, including without limitation
014: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
015: * and/or sell copies of the Software, and to permit persons to whom the
016: * Software is furnished to do so, subject to the following conditions:
017: *
018: * The above copyright notice and this permission notice shall be included in
019: * all copies or substantial portions of the Software.
020: *
021: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
022: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
023: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
024: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
025: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
026: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
027: * DEALINGS IN THE SOFTWARE.
028: */
029: package net.sourceforge.groboutils.autodoc.v1;
030:
031: import net.sourceforge.groboutils.util.classes.v1.SPISingletonStore;
032:
033: import net.sourceforge.groboutils.autodoc.v1.defimpl.DefaultAutoDocFactory;
034: import net.sourceforge.groboutils.autodoc.v1.defimpl.AutoDocTPSet;
035: import net.sourceforge.groboutils.autodoc.v1.defimpl.AutoDocLogSet;
036: import net.sourceforge.groboutils.autodoc.v1.defimpl.AutoDocITSet;
037:
038: import net.sourceforge.groboutils.autodoc.v1.spi.AutoDocFactory;
039:
040: import java.util.Vector;
041:
042: /**
043: * Entry class for loading the AutoDoc pieces for a particular class. As
044: * this class is always instantiated, it does not need an interface. This
045: * acts as a central factory for generating all the AutoDoc instances.
046: * <P>
047: * This default implementation uses a static factory to load each instance's
048: * data. That factory may be set by a using class. Since initialization of
049: * the inner data is lazy-loaded (only loaded when needed, but when needed it
050: * is all loaded), the factory may be set at any time after construction, and
051: * before a data access member function is called. If no factory is ever
052: * specified, it will load the factory from the fully-qualified class name
053: * in the value of the System property specified by the key given by
054: * <tt>FACTORY_PROPERTY_NAME</tt>.
055: * <P>
056: * This class follows a combination of the Abstract Factory and Proxy patterns.
057: * AutoDoc acts as a Proxy for an Abstract Factory, allowing the particular
058: * implemented factory to be hidden from the user. However, a "hole" is open
059: * to each instance with the method <tt>setFactory()</tt>, allowing the owner
060: * to setup the factory to their needs.
061: * <P>
062: * NOTE: need to update the documentation. I've since replaced the single
063: * factory with an SPI fleet. The proxied objects are delegators to collections
064: * of sub-proxies, which are loaded as late as possible.
065: *
066: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
067: * @version $Date: 2003/02/10 22:52:11 $
068: * @since March 16, 2002
069: */
070: public class AutoDoc {
071: private static final SPISingletonStore s_factoryStore = new SPISingletonStore(
072: AutoDocFactory.class, SPISingletonStore.NO_MULTIPLES_SILENT);
073:
074: private volatile boolean notLoaded = true;
075: private Vector factories = null;
076: private final Class owner;
077: private AutoDocLogSet log = new AutoDocLogSet();
078: private AutoDocITSet it = new AutoDocITSet();
079: private AutoDocTPSet tp = new AutoDocTPSet();
080:
081: /**
082: * Base constructor. Pass in the Class instance which will AutoDoc itself.
083: *
084: * @param c the class which will be AutoDoced.
085: * @exception IllegalArgumentException if the passed-in class is
086: * <tt>null</tt>.
087: */
088: public AutoDoc(Class c) {
089: if (c == null) {
090: throw new IllegalArgumentException("no null arguments");
091: }
092: this .owner = c;
093:
094: // lazy load data.
095: }
096:
097: /**
098: * Get the log implementation. Each call will return the same object.
099: *
100: * @return the log for the owning class.
101: */
102: public AutoDocLog getLog() {
103: checkLoad();
104: return this .log;
105: }
106:
107: /**
108: * Get the Issue Tracker implementation. Each call will return the
109: * same object.
110: *
111: * @return the Issue Tracker for the owning class.
112: */
113: public AutoDocIT getIT() {
114: checkLoad();
115: return this .it;
116: }
117:
118: /**
119: * Get the Test Procedure Marker implementation. Each call will return
120: * the same object.
121: */
122: public AutoDocTP getTP() {
123: checkLoad();
124: return this .tp;
125: }
126:
127: //-------------------------------------------------------------------------
128: // Protected methods
129:
130: /**
131: * Check if the data instances have been loaded. If they have not, then
132: * load the instances, and ensure initialization is not performed again.
133: * This lazy-loading is thread-safe.
134: */
135: protected final void checkLoad() {
136: if (this .notLoaded) {
137: // double-check locking pattern.
138: // since we're doing this on an atomicly volitile variable,
139: // this is fine. If we did this on a non-atomic variable, this
140: // might fail.
141: synchronized (this .getClass()) {
142: if (this .notLoaded) {
143: loadInstances();
144: this .notLoaded = false;
145: }
146: }
147: }
148: }
149:
150: /**
151: * Retrieves the owning class. Will never return <tt>null</tt>.
152: *
153: * @return the class which is being AutoDoced.
154: */
155: protected final Class getOwner() {
156: return this .owner;
157: }
158:
159: /**
160: * Loads all class
161: */
162: protected void loadInstances() {
163: AutoDocFactory adf[] = getFactories();
164: for (int i = 0; i < adf.length; ++i) {
165: loadFromFactory(adf[i]);
166: }
167: cleanUpFactories();
168: }
169:
170: /**
171: * Adds to all inner instances from the given factory.
172: */
173: protected void loadFromFactory(AutoDocFactory adf) {
174: this .log.addLog(adf.createLog(getOwner()));
175: this .it.addIT(adf.createIT(getOwner()));
176: this .tp.addTP(adf.createTP(getOwner()));
177: }
178:
179: /**
180: * Returns the factory this implementation knows. By default, this is the
181: * static factory instance. This is only called at AutoDoc creation time.
182: * This is not static, because subclasses may need to re-implement this
183: * method. The factory may be changed through invocation of
184: * <tt>setFactory( AutoDocFactory )</tt>.
185: *
186: * @return the inner factory, or the static singleton if it was never
187: * assigned.
188: */
189: protected AutoDocFactory[] getFactories()
190: {
191: AutoDocFactory adf[];
192: synchronized( this )
193: {
194: if (this .factories == null)
195: {
196: this .factories = new Vector();
197: java.util.Enumeration enum = getFactoryStore().getSingletons();
198: while (enum.hasMoreElements())
199: {
200: addFactory( (AutoDocFactory)enum.nextElement() );
201: }
202: }
203: adf = new AutoDocFactory[ this .factories.size() ];
204: this .factories.copyInto( adf );
205: }
206: return adf;
207: }
208:
209: /**
210: * After loading the instances, we have no need to keep the memory
211: * of the original factories around - they may pollute our loaded
212: * proxied objects anyway. But since we've loaded, we don't want
213: * to allow another load. So keep the vector, but clean it out.
214: */
215: protected void cleanUpFactories() {
216: this .factories.removeAllElements();
217: }
218:
219: /**
220: * Sets the AutoDoc factory instance. If this method is never invoked, then
221: * the default static store will be used instead.
222: * <P>
223: * The standard factory/singleton pattern used in this framework normally
224: * does not allow direct setting of the framework for an instance, but
225: * rather for the classloader's class. Since AutoDoc (or a subclass)
226: * is directly instantiated as a central processing point, opening a
227: * "hole" into the class allows for an easier method to setup a particular
228: * AutoDoc style. However, this is not the "recommended" usage, since,
229: * in general, AutoDoc instances are instantiated independently throughout
230: * many different classes, causing independent factory setting to be
231: * more difficult.
232: *
233: * @param adf the new factory to assign.
234: * @exception IllegalArgumentException if <tt>adf</tt> is <tt>null</tt>.
235: * @exception IllegalStateException if the inner factory has already been
236: * assigned. This usually indicates that all the objects have already
237: * been loaded.
238: */
239: protected void addFactory(AutoDocFactory adf) {
240: if (adf == null) {
241: throw new IllegalArgumentException("no null args");
242: }
243: synchronized (this ) {
244: if (!this .notLoaded) {
245: throw new IllegalStateException(
246: "factories already loaded.");
247: }
248: if (this .factories == null) {
249: // allow this - it means we will not be using the defaults.
250: this .factories = new Vector();
251: }
252: this .factories.addElement(adf);
253: }
254: }
255:
256: //-------------------------------------------------------------------------
257: // Static methods
258:
259: /**
260: * Retrieve the AutoDocFactory singleton store for setting up the factory
261: * to be used for all uninitialized or uncreated AutoDoc instances.
262: */
263: public static SPISingletonStore getFactoryStore() {
264: return s_factoryStore;
265: }
266: }
|