001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.ws.policy;
038:
039: import java.util.Collection;
040: import java.util.Collections;
041: import java.util.Comparator;
042: import java.util.Iterator;
043: import java.util.Set;
044: import java.util.TreeSet;
045:
046: import javax.xml.namespace.QName;
047:
048: import com.sun.xml.ws.policy.privateutil.LocalizationMessages;
049: import com.sun.xml.ws.policy.privateutil.PolicyUtils;
050: import java.util.LinkedList;
051: import java.util.List;
052:
053: /**
054: * The AssertionSet is a set of assertions. It represents a single policy alternative.
055: *
056: * @author Fabian Ritzmann, Marek Potociar
057: */
058: public final class AssertionSet implements Iterable<PolicyAssertion>,
059: Comparable<AssertionSet> {
060: private static final AssertionSet EMPTY_ASSERTION_SET = new AssertionSet(
061: Collections
062: .unmodifiableList(new LinkedList<PolicyAssertion>()));
063: /**
064: * The comparator comapres policy assertions according to their publicly accessible attributes, in the following
065: * order of attributes:
066: *
067: * 1. namespace (not null String)
068: * 2. local name (not null String)
069: * 3. value (String): null < "" < "not empty"
070: * 4. has nested assertions (boolean): false < true
071: * 5. has nested policy (boolean): false < true
072: * 6. hashCode comparison
073: */
074: private static final Comparator<PolicyAssertion> ASSERTION_COMPARATOR = new Comparator<PolicyAssertion>() {
075: public int compare(final PolicyAssertion pa1,
076: final PolicyAssertion pa2) {
077: if (pa1.equals(pa2)) {
078: return 0;
079: }
080:
081: int result;
082:
083: result = PolicyUtils.Comparison.QNAME_COMPARATOR.compare(
084: pa1.getName(), pa2.getName());
085: if (result != 0) {
086: return result;
087: }
088:
089: result = PolicyUtils.Comparison.compareNullableStrings(pa1
090: .getValue(), pa2.getValue());
091: if (result != 0) {
092: return result;
093: }
094:
095: result = PolicyUtils.Comparison.compareBoolean(pa1
096: .hasNestedAssertions(), pa2.hasNestedAssertions());
097: if (result != 0) {
098: return result;
099: }
100:
101: result = PolicyUtils.Comparison.compareBoolean(pa1
102: .hasNestedPolicy(), pa2.hasNestedPolicy());
103: if (result != 0) {
104: return result;
105: }
106:
107: return Math.round(Math.signum(pa1.hashCode()
108: - pa2.hashCode()));
109: }
110: };
111:
112: private final List<PolicyAssertion> assertions;
113: private final Set<QName> vocabulary = new TreeSet<QName>(
114: PolicyUtils.Comparison.QNAME_COMPARATOR);
115: private final Collection<QName> immutableVocabulary = Collections
116: .unmodifiableCollection(vocabulary);
117:
118: private AssertionSet(List<PolicyAssertion> list) {
119: assert (list != null) : LocalizationMessages
120: .WSP_0037_PRIVATE_CONSTRUCTOR_DOES_NOT_TAKE_NULL();
121: this .assertions = list;
122: }
123:
124: private AssertionSet(final Collection<AssertionSet> alternatives) {
125: this .assertions = new LinkedList<PolicyAssertion>();
126: for (AssertionSet alternative : alternatives) {
127: addAll(alternative.assertions);
128: }
129: }
130:
131: private boolean add(final PolicyAssertion assertion) {
132: if (assertion == null) {
133: return false;
134: }
135:
136: if (this .assertions.contains(assertion)) {
137: return false;
138: } else {
139: this .assertions.add(assertion);
140: this .vocabulary.add(assertion.getName());
141: return true;
142: }
143: }
144:
145: private boolean addAll(
146: final Collection<? extends PolicyAssertion> assertions) {
147: boolean result = true;
148:
149: if (assertions != null) {
150: for (PolicyAssertion assertion : assertions) {
151: result &= add(assertion); // this is here to ensure that vocabulary is built correctly as well
152: }
153: }
154:
155: return result;
156: }
157:
158: /**
159: * Return all assertions contained in this assertion set.
160: *
161: * @return All assertions contained in this assertion set
162: */
163: Collection<PolicyAssertion> getAssertions() {
164: return assertions;
165: }
166:
167: /**
168: * Retrieves the vocabulary of this policy expression. The vocabulary is represented by an immutable collection of
169: * unique QName objects. Each of those objects represents single assertion type contained in the assertion set.
170: *
171: * @return immutable collection of assertion types contained in the assertion set (a policy vocabulary).
172: */
173: Collection<QName> getVocabulary() {
174: return immutableVocabulary;
175: }
176:
177: /**
178: * Checks whether this policy alternative is compatible with the provided policy alternative.
179: *
180: * @param alternative policy alternative used for compatibility test
181: * @param mode compatibility mode to be used
182: * @return {@code true} if the two policy alternatives are compatible, {@code false} otherwise
183: */
184: boolean isCompatibleWith(final AssertionSet alternative,
185: PolicyIntersector.CompatibilityMode mode) {
186: boolean result = (mode == PolicyIntersector.CompatibilityMode.LAX)
187: || this .vocabulary.equals(alternative.vocabulary);
188:
189: result = result
190: && this .areAssertionsCompatible(alternative, mode);
191: result = result
192: && alternative.areAssertionsCompatible(this , mode);
193:
194: return result;
195: }
196:
197: private boolean areAssertionsCompatible(
198: final AssertionSet alternative,
199: PolicyIntersector.CompatibilityMode mode) {
200: nextAssertion: for (PolicyAssertion this Assertion : this .assertions) {
201: if ((mode == PolicyIntersector.CompatibilityMode.STRICT)
202: || !this Assertion.isIgnorable()) {
203: for (PolicyAssertion thatAssertion : alternative.assertions) {
204: if (this Assertion.isCompatibleWith(thatAssertion,
205: mode)) {
206: continue nextAssertion;
207: }
208: }
209: return false;
210: }
211: }
212: return true;
213: }
214:
215: /**
216: * Creates and returns new assertion set holding content of all provided policy assertion sets.
217: * <p/>
218: * This method should not be used to perform a merge of general Policy instances. A client should be aware of the
219: * method's result meaning and the difference between merge of Policy instances and merge of AssertionSet instances.
220: *
221: *
222: * @param alternatives collection of provided policy assertion sets which content is to be stored in the assertion set.
223: * May be {@code null} - empty assertion set is returned in such case.
224: * @return new instance of assertion set holding the content of all provided policy assertion sets.
225: */
226: public static AssertionSet createMergedAssertionSet(
227: final Collection<AssertionSet> alternatives) {
228: if (alternatives == null || alternatives.isEmpty()) {
229: return EMPTY_ASSERTION_SET;
230: }
231:
232: final AssertionSet result = new AssertionSet(alternatives);
233: Collections.sort(result.assertions, ASSERTION_COMPARATOR);
234:
235: return result;
236: }
237:
238: /**
239: * Creates and returns new assertion set holding a set of provided policy assertions.
240: *
241: * @param assertions collection of provided policy assertions to be stored in the assertion set. May be {@code null}.
242: * @return new instance of assertion set holding the provided policy assertions
243: */
244: public static AssertionSet createAssertionSet(
245: final Collection<? extends PolicyAssertion> assertions) {
246: if (assertions == null || assertions.isEmpty()) {
247: return EMPTY_ASSERTION_SET;
248: }
249:
250: final AssertionSet result = new AssertionSet(
251: new LinkedList<PolicyAssertion>());
252: result.addAll(assertions);
253: Collections.sort(result.assertions, ASSERTION_COMPARATOR);
254:
255: return result;
256: }
257:
258: public static AssertionSet emptyAssertionSet() {
259: return EMPTY_ASSERTION_SET;
260: }
261:
262: /**
263: * Returns an iterator over a set of child policy assertion objects.
264: *
265: * @return policy assertion Iterator.
266: */
267: public Iterator<PolicyAssertion> iterator() {
268: return this .assertions.iterator();
269: }
270:
271: /**
272: * Searches for assertions with given name. Only assertions that are contained as immediate children of the assertion set are
273: * searched, i.e. nested policies are not searched.
274: *
275: * @param name The fully qualified name of searched assertion
276: * @return List of all assertions matching the requested name. If no assertions are found, the returned list is empty
277: * (i.e. {@code null} value is never returned).
278: */
279: public Collection<PolicyAssertion> get(final QName name) {
280: final List<PolicyAssertion> matched = new LinkedList<PolicyAssertion>();
281:
282: if (vocabulary.contains(name)) {
283: // we iterate the assertion set only if we are sure we contain such assertion name in our vocabulary
284: for (PolicyAssertion assertion : assertions) {
285: if (assertion.getName().equals(name)) {
286: matched.add(assertion);
287: }
288: }
289: }
290:
291: return matched;
292: }
293:
294: /**
295: * Returns {@code true} if this assertion set contains no assertions.
296: *
297: * @return {@code true} if this assertion set contains no assertions.
298: */
299: public boolean isEmpty() {
300: return assertions.isEmpty();
301: }
302:
303: /**
304: * Returns true if the assertion set contains the assertion name specified in its vocabulary
305: *
306: * @param assertionName the fully qualified name of the assertion
307: * @return {@code true}, if an assertion with the given name could be found in the assertion set vocabulary {@code false} otherwise.
308: */
309: public boolean contains(final QName assertionName) {
310: return vocabulary.contains(assertionName);
311: }
312:
313: /**
314: * An {@code Comparable<T>.compareTo(T o)} interface method implementation.
315: * @param that other alternative to compare with
316: */
317: public int compareTo(final AssertionSet that) {
318: if (this .equals(that)) {
319: return 0;
320: }
321:
322: // comparing vocabularies
323: final Iterator<QName> vIterator1 = this .getVocabulary()
324: .iterator();
325: final Iterator<QName> vIterator2 = that.getVocabulary()
326: .iterator();
327: while (vIterator1.hasNext()) {
328: final QName entry1 = vIterator1.next();
329: if (vIterator2.hasNext()) {
330: final QName entry2 = vIterator2.next();
331: final int result = PolicyUtils.Comparison.QNAME_COMPARATOR
332: .compare(entry1, entry2);
333: if (result != 0) {
334: return result;
335: }
336: } else {
337: return 1; // we have more entries in this vocabulary
338: }
339: }
340:
341: if (vIterator2.hasNext()) {
342: return -1; // we have more entries in that vocabulary
343: }
344:
345: // vocabularies are equal => comparing assertions
346: final Iterator<PolicyAssertion> pIterator1 = this
347: .getAssertions().iterator();
348: final Iterator<PolicyAssertion> pIterator2 = that
349: .getAssertions().iterator();
350: while (pIterator1.hasNext()) {
351: final PolicyAssertion pa1 = pIterator1.next();
352: if (pIterator2.hasNext()) {
353: final PolicyAssertion pa2 = pIterator2.next();
354: final int result = ASSERTION_COMPARATOR.compare(pa1,
355: pa2);
356: if (result != 0) {
357: return result;
358: }
359: } else {
360: return 1; // we have more entries in this assertion set
361: }
362: }
363:
364: if (pIterator2.hasNext()) {
365: return -1; // we have more entries in that assertion set
366: }
367:
368: // seems like objects are very simmilar although not equal => we must not return 0 otherwise the TreeSet
369: // holding this element would discard the newly added element. Thus we return that the first argument is
370: // greater than second (just because it is first...)
371: return 1;
372: }
373:
374: /**
375: * An {@code Object.equals(Object obj)} method override.
376: */
377: public boolean equals(final Object obj) {
378: if (this == obj) {
379: return true;
380: }
381:
382: if (!(obj instanceof AssertionSet)) {
383: return false;
384: }
385:
386: final AssertionSet that = (AssertionSet) obj;
387: boolean result = true;
388:
389: result = result && this .vocabulary.equals(that.vocabulary);
390: result = result
391: && this .assertions.size() == that.assertions.size()
392: && this .assertions.containsAll(that.assertions);
393:
394: return result;
395: }
396:
397: /**
398: * An {@code Object.hashCode()} method override.
399: */
400: public int hashCode() {
401: int result = 17;
402:
403: result = 37 * result + vocabulary.hashCode();
404: result = 37 * result + assertions.hashCode();
405:
406: return result;
407: }
408:
409: /**
410: * An {@code Object.toString()} method override.
411: */
412: public String toString() {
413: return toString(0, new StringBuffer()).toString();
414: }
415:
416: /**
417: * A helper method that appends indented string representation of this instance to the input string buffer.
418: *
419: * @param indentLevel indentation level to be used.
420: * @param buffer buffer to be used for appending string representation of this instance
421: * @return modified buffer containing new string representation of the instance
422: */
423: StringBuffer toString(final int indentLevel,
424: final StringBuffer buffer) {
425: final String indent = PolicyUtils.Text
426: .createIndent(indentLevel);
427: final String innerIndent = PolicyUtils.Text
428: .createIndent(indentLevel + 1);
429:
430: buffer.append(indent).append("assertion set {").append(
431: PolicyUtils.Text.NEW_LINE);
432:
433: if (assertions.isEmpty()) {
434: buffer.append(innerIndent).append("no assertions").append(
435: PolicyUtils.Text.NEW_LINE);
436: } else {
437: for (PolicyAssertion assertion : assertions) {
438: assertion.toString(indentLevel + 1, buffer).append(
439: PolicyUtils.Text.NEW_LINE);
440: }
441: }
442:
443: buffer.append(indent).append('}');
444:
445: return buffer;
446: }
447: }
|