1// =================================================================================================
2// ADOBE SYSTEMS INCORPORATED
3// Copyright 2006 Adobe Systems Incorporated
4// All Rights Reserved
5//
6// NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
7// of the Adobe license agreement accompanying it.
8// =================================================================================================
9
10package com.adobe.xmp.options;
11
12import java.util.HashMap;
13import java.util.Map;
14
15import com.adobe.xmp.XMPError;
16import com.adobe.xmp.XMPException;
17
18/**
19 * The base class for a collection of 32 flag bits. Individual flags are defined as enum value bit
20 * masks. Inheriting classes add convenience accessor methods.
21 *
22 * @since 24.01.2006
23 */
24public abstract class Options
25{
26	/** the internal int containing all options */
27	private int options = 0;
28	/** a map containing the bit names */
29	private Map optionNames = null;
30
31
32	/**
33	 * The default constructor.
34	 */
35	public Options()
36	{
37		// EMTPY
38	}
39
40
41	/**
42	 * Constructor with the options bit mask.
43	 *
44	 * @param options the options bit mask
45	 * @throws XMPException If the options are not correct
46	 */
47	public Options(int options) throws XMPException
48	{
49		assertOptionsValid(options);
50		setOptions(options);
51	}
52
53
54	/**
55	 * Resets the options.
56	 */
57	public void clear()
58	{
59		options = 0;
60	}
61
62
63	/**
64	 * @param optionBits an option bitmask
65	 * @return Returns true, if this object is equal to the given options.
66	 */
67	public boolean isExactly(int optionBits)
68	{
69		return getOptions() == optionBits;
70	}
71
72
73	/**
74	 * @param optionBits an option bitmask
75	 * @return Returns true, if this object contains all given options.
76	 */
77	public boolean containsAllOptions(int optionBits)
78	{
79		return (getOptions() & optionBits) == optionBits;
80	}
81
82
83	/**
84	 * @param optionBits an option bitmask
85	 * @return Returns true, if this object contain at least one of the given options.
86	 */
87	public boolean containsOneOf(int optionBits)
88	{
89		return ((getOptions()) & optionBits) != 0;
90	}
91
92
93	/**
94	 * @param optionBit the binary bit or bits that are requested
95	 * @return Returns if <emp>all</emp> of the requested bits are set or not.
96	 */
97	protected boolean getOption(int optionBit)
98	{
99		return (options & optionBit) != 0;
100	}
101
102
103	/**
104	 * @param optionBits the binary bit or bits that shall be set to the given value
105	 * @param value the boolean value to set
106	 */
107	public void setOption(int optionBits, boolean value)
108	{
109		options = value ? options | optionBits : options & ~optionBits;
110	}
111
112
113	/**
114	 * Is friendly to access it during the tests.
115	 * @return Returns the options.
116	 */
117	public int getOptions()
118	{
119		return options;
120	}
121
122
123	/**
124	 * @param options The options to set.
125	 * @throws XMPException
126	 */
127	public void setOptions(int options) throws XMPException
128	{
129		assertOptionsValid(options);
130		this.options = options;
131	}
132
133
134	/**
135	 * @see Object#equals(Object)
136	 */
137	public boolean equals(Object obj)
138	{
139		return getOptions() == ((Options) obj).getOptions();
140	}
141
142
143	/**
144	 * @see java.lang.Object#hashCode()
145	 */
146	public int hashCode()
147	{
148		return getOptions();
149	}
150
151
152	/**
153	 * Creates a human readable string from the set options. <em>Note:</em> This method is quite
154	 * expensive and should only be used within tests or as
155	 * @return Returns a String listing all options that are set to <code>true</code> by their name,
156	 * like &quot;option1 | option4&quot;.
157	 */
158	public String getOptionsString()
159	{
160		if (options != 0)
161		{
162			StringBuffer sb = new StringBuffer();
163			int theBits = options;
164			while (theBits != 0)
165			{
166				int oneLessBit = theBits & (theBits - 1); // clear rightmost one bit
167				int singleBit = theBits ^ oneLessBit;
168				String bitName = getOptionName(singleBit);
169				sb.append(bitName);
170				if (oneLessBit != 0)
171				{
172					sb.append(" | ");
173				}
174				theBits = oneLessBit;
175			}
176			return sb.toString();
177		}
178		else
179		{
180			return "<none>";
181		}
182	}
183
184
185	/**
186	 * @return Returns the options as hex bitmask.
187	 */
188	public String toString()
189	{
190		return "0x" + Integer.toHexString(options);
191	}
192
193
194	/**
195	 * To be implemeted by inheritants.
196	 * @return Returns a bit mask where all valid option bits are set.
197	 */
198	protected abstract int getValidOptions();
199
200
201	/**
202	 * To be implemeted by inheritants.
203	 * @param option a single, valid option bit.
204	 * @return Returns a human readable name for an option bit.
205	 */
206	protected abstract String defineOptionName(int option);
207
208
209	/**
210	 * The inheriting option class can do additional checks on the options.
211	 * <em>Note:</em> For performance reasons this method is only called
212	 * when setting bitmasks directly.
213	 * When get- and set-methods are used, this method must be called manually,
214	 * normally only when the Options-object has been created from a client
215	 * (it has to be made public therefore).
216	 *
217	 * @param options the bitmask to check.
218	 * @throws XMPException Thrown if the options are not consistent.
219	 */
220	protected void assertConsistency(int options) throws XMPException
221	{
222		// empty, no checks
223	}
224
225
226	/**
227	 * Checks options before they are set.
228	 * First it is checked if only defined options are used,
229	 * second the additional {@link Options#assertConsistency(int)}-method is called.
230	 *
231	 * @param options the options to check
232	 * @throws XMPException Thrown if the options are invalid.
233	 */
234	private void assertOptionsValid(int options) throws XMPException
235	{
236		int invalidOptions = options & ~getValidOptions();
237		if (invalidOptions == 0)
238		{
239			assertConsistency(options);
240		}
241		else
242		{
243			throw new XMPException("The option bit(s) 0x" + Integer.toHexString(invalidOptions)
244					+ " are invalid!", XMPError.BADOPTIONS);
245		}
246	}
247
248
249
250	/**
251	 * Looks up or asks the inherited class for the name of an option bit.
252	 * Its save that there is only one valid option handed into the method.
253	 * @param option a single option bit
254	 * @return Returns the option name or undefined.
255	 */
256	private String getOptionName(int option)
257	{
258		Map optionsNames = procureOptionNames();
259
260		Integer key = new Integer(option);
261		String result = (String) optionsNames.get(key);
262		if (result == null)
263		{
264			result = defineOptionName(option);
265			if (result != null)
266			{
267				optionsNames.put(key, result);
268			}
269			else
270			{
271				result = "<option name not defined>";
272			}
273		}
274
275		return result;
276	}
277
278
279	/**
280	 * @return Returns the optionNames map and creates it if required.
281	 */
282	private Map procureOptionNames()
283	{
284		if (optionNames == null)
285		{
286			optionNames = new HashMap();
287		}
288		return optionNames;
289	}
290}
291