648 lines
24 KiB
Java
648 lines
24 KiB
Java
|
|
/*
|
|
* @(#)PolicyReference.java
|
|
*
|
|
* Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistribution of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistribution in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
|
|
* be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* This software is provided "AS IS," without a warranty of any kind. ALL
|
|
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
|
|
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
|
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
|
|
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
|
|
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
|
|
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
|
|
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
|
|
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
|
|
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
|
|
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
|
*
|
|
* You acknowledge that this software is not designed or intended for use in
|
|
* the design, construction, operation or maintenance of any nuclear facility.
|
|
*/
|
|
|
|
package com.sun.xacml;
|
|
|
|
import com.sun.xacml.combine.CombinerElement;
|
|
import com.sun.xacml.combine.CombiningAlgorithm;
|
|
|
|
import com.sun.xacml.ctx.PolicyIssuer;
|
|
import com.sun.xacml.ctx.Result;
|
|
import com.sun.xacml.ctx.Status;
|
|
import com.sun.xacml.debug.RuntimeInfo;
|
|
import com.sun.xacml.debug.RuntimeInfo.ELEMENT_TYPE;
|
|
|
|
import com.sun.xacml.finder.PolicyFinder;
|
|
import com.sun.xacml.finder.PolicyFinderResult;
|
|
|
|
import java.io.OutputStream;
|
|
import java.io.PrintStream;
|
|
import java.io.UnsupportedEncodingException;
|
|
|
|
import java.net.URI;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.Vector;
|
|
|
|
import org.apache.log4j.Logger;
|
|
|
|
import org.w3c.dom.NamedNodeMap;
|
|
import org.w3c.dom.Node;
|
|
|
|
|
|
/**
|
|
* This class is used as a placeholder for the PolicyIdReference and
|
|
* PolicySetIdReference fields in a PolicySetType. When a reference is used
|
|
* in a policy set, it is telling the PDP to use an external policy in
|
|
* the current policy. Each time the PDP needs to evaluate that policy
|
|
* reference, it asks the policy finder for the policy. Typically the policy
|
|
* finder will have cached the referenced policy, so this isn't too slow.
|
|
* <p>
|
|
* NOTE: all of the accessor methods, the match method, and the evaluate method
|
|
* require this class to ask its <code>PolicyFinder</code> for the referenced
|
|
* policy, which can be a slow operation. Care should be taken, therefore in
|
|
* calling these methods too often. Also note that it's not safe to cache the
|
|
* results of these calls, since the referenced policy may change.
|
|
*
|
|
* @since 1.0
|
|
* @author Seth Proctor
|
|
* @author Ludwig Seitz
|
|
*/
|
|
public class PolicyReference extends AbstractPolicy {
|
|
|
|
/**
|
|
* Identifies this as a reference to a <code>Policy</code>
|
|
*/
|
|
public static final int POLICY_REFERENCE = 0;
|
|
|
|
/**
|
|
* Identifies this as a reference to a <code>PolicySet</code>
|
|
*/
|
|
public static final int POLICYSET_REFERENCE = 1;
|
|
|
|
// the reference
|
|
private URI reference;
|
|
|
|
// the reference type
|
|
private int policyType;
|
|
|
|
// and version constraints on this reference
|
|
private VersionConstraints constraints;
|
|
|
|
// the finder to use in finding the referenced policy
|
|
private PolicyFinder finder;
|
|
|
|
// the meta-data for the parent policy
|
|
private PolicyMetaData parentMetaData;
|
|
|
|
// the referenced policy
|
|
private AbstractPolicy referencedPolicy;
|
|
|
|
// the logger we'll use for all messages
|
|
private static final Logger logger =
|
|
Logger.getLogger(PolicyReference.class.getName());
|
|
|
|
/**
|
|
* Creates a new <code>PolicyReference</code> instance. This has no
|
|
* constraints on version matching. Note that an XACML 1.x reference may
|
|
* not have any constraints.
|
|
*
|
|
* @param reference the reference to the policy
|
|
* @param policyType one of the two fields in this class
|
|
* @param finder the <code>PolicyFinder</code> used to handle the reference
|
|
* @param parentMetaData the meta-data associated with the containing
|
|
* (parent) policy
|
|
*
|
|
* @throws IllegalArgumentException if the input policyType isn't valid
|
|
*/
|
|
public PolicyReference(URI reference, int policyType, PolicyFinder finder,
|
|
PolicyMetaData parentMetaData)
|
|
throws IllegalArgumentException
|
|
{
|
|
this(reference, policyType, new VersionConstraints(null, null, null),
|
|
finder, parentMetaData);
|
|
}
|
|
|
|
/**
|
|
* Creates a new <code>PolicyReference</code> instance with version
|
|
* constraints. Note that an XACML 1.x reference may not have any
|
|
* constraints.
|
|
*
|
|
* @param reference the reference to the policy
|
|
* @param policyType one of the two fields in this class
|
|
* @param constraints any optional constraints on the version of the
|
|
* referenced policy (this is never null, but
|
|
* it may impose no constraints, and in fact will
|
|
* never impose constraints when used from a pre-2.0
|
|
* XACML policy)
|
|
* @param finder the <code>PolicyFinder</code> used to handle the reference
|
|
* @param parentMetaData the meta-data associated with the containing
|
|
* (parent) policy
|
|
*
|
|
* @throws IllegalArgumentException if the input policyType isn't valid
|
|
*/
|
|
public PolicyReference(URI reference, int policyType,
|
|
VersionConstraints constraints, PolicyFinder finder,
|
|
PolicyMetaData parentMetaData)
|
|
throws IllegalArgumentException {
|
|
|
|
// check if input policyType is a valid value
|
|
if ((policyType != POLICY_REFERENCE) &&
|
|
(policyType != POLICYSET_REFERENCE)) {
|
|
throw new IllegalArgumentException("Input policyType is not a" +
|
|
"valid value");
|
|
}
|
|
this.reference = reference;
|
|
this.policyType = policyType;
|
|
this.constraints = constraints;
|
|
this.finder = finder;
|
|
this.parentMetaData = parentMetaData;
|
|
}
|
|
|
|
/**
|
|
* The clone method.
|
|
* FIXME: this does no deep copy on the
|
|
* contraints, finder and parentMetaData.
|
|
* Should probably _not_ do a deep copy on finder.
|
|
*
|
|
* @return a copy of this object.
|
|
*/
|
|
public Object clone() {
|
|
PolicyReference clone = (PolicyReference)super.clone();
|
|
clone.reference = this.reference;
|
|
clone.policyType = this.policyType;
|
|
clone.constraints = this.constraints;
|
|
clone.finder = this.finder;
|
|
clone.parentMetaData = this.parentMetaData;
|
|
return clone;
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates an instance of a <code>PolicyReference</code> object based on
|
|
* a DOM node.
|
|
*
|
|
* @deprecated As of 2.0 you should avoid using this method and should
|
|
* instead use the version that takes a
|
|
* <code>PolicyMetaData</code> instance. This method will
|
|
* only work for XACML 1.x policies.
|
|
*
|
|
* @param root the DOM root of a PolicyIdReference or a
|
|
* PolicySetIdReference XML type
|
|
* @param finder the <code>PolicyFinder</code> used to handle the reference
|
|
*
|
|
* @return The PolicyReference object.
|
|
*
|
|
* @exception ParsingException if the node is invalid
|
|
*/
|
|
public static PolicyReference getInstance(Node root, PolicyFinder finder)
|
|
throws ParsingException
|
|
{
|
|
return getInstance(root, finder, new PolicyMetaData());
|
|
}
|
|
|
|
/**
|
|
* Creates an instance of a <code>PolicyReference</code> object based on
|
|
* a DOM node.
|
|
*
|
|
* @param root the DOM root of a PolicyIdReference or a
|
|
* PolicySetIdReference XML type
|
|
* @param finder the <code>PolicyFinder</code> used to handle the reference
|
|
* @param metaData the meta-data associated with the containing policy
|
|
*
|
|
* @return The PolicyReference object.
|
|
*
|
|
* @exception ParsingException if the node is invalid
|
|
*/
|
|
public static PolicyReference getInstance(Node root, PolicyFinder finder,
|
|
PolicyMetaData metaData)
|
|
throws ParsingException
|
|
{
|
|
URI reference = null;
|
|
int policyType;
|
|
if (root.getNodeType() != Node.ELEMENT_NODE) {
|
|
throw new ParsingException("Cannot build a PolicyReference"
|
|
+ "with a: " + root.getClass().getName() +" XML node" );
|
|
}
|
|
// see what type of reference we are
|
|
String name = root.getLocalName();
|
|
if (name.equals("PolicyIdReference")) {
|
|
policyType = POLICY_REFERENCE;
|
|
} else if (name.equals("PolicySetIdReference")) {
|
|
policyType = POLICYSET_REFERENCE;
|
|
} else {
|
|
throw new ParsingException("Unknown reference type: " + name);
|
|
}
|
|
|
|
// next get the reference
|
|
try {
|
|
reference = new URI(root.getFirstChild().getNodeValue());
|
|
} catch (Exception e) {
|
|
throw new ParsingException("Error while parsing Reference", e);
|
|
}
|
|
|
|
// now get any constraints
|
|
NamedNodeMap map = root.getAttributes();
|
|
String versionConstraint = null;
|
|
String earlyConstraint = null;
|
|
String lateConstraint = null;
|
|
|
|
Node versionNode = map.getNamedItem("Version");
|
|
if (versionNode != null) {
|
|
versionConstraint = versionNode.getNodeValue();
|
|
}
|
|
|
|
Node earlyNode = map.getNamedItem("EarliestVersion");
|
|
if (earlyNode != null) {
|
|
earlyConstraint = earlyNode.getNodeValue();
|
|
}
|
|
|
|
Node lateNode = map.getNamedItem("LatestVersion");
|
|
if (lateNode != null) {
|
|
lateConstraint = lateNode.getNodeValue();
|
|
}
|
|
|
|
VersionConstraints constraints =
|
|
new VersionConstraints(versionConstraint, earlyConstraint,
|
|
lateConstraint);
|
|
|
|
// finally, create the reference
|
|
PolicyReference policyReference = new PolicyReference(
|
|
reference, policyType, constraints, finder, metaData);
|
|
policyReference.src = RuntimeInfo.getRuntimeInfo(policyReference, root, ELEMENT_TYPE.POLICY_REFERENCE);
|
|
//policyReference.setSourceLocator(RuntimeInfo.getRuntimeInfo(policyReference, root, ELEMENT_TYPE.POLICY_REFERENCE));
|
|
return policyReference;
|
|
}
|
|
|
|
/**
|
|
* Returns the refernce identitfier used to resolve the policy.
|
|
*
|
|
* @return the reference <code>URI</code>
|
|
*/
|
|
public URI getReference() {
|
|
return this.reference;
|
|
}
|
|
|
|
/**
|
|
* Returns the version constraints associated with this reference. This
|
|
* will never be null, though the constraints may be empty.
|
|
*
|
|
* @return the version constraints
|
|
*/
|
|
public VersionConstraints getConstraints() {
|
|
return this.constraints;
|
|
}
|
|
|
|
/**
|
|
* Returns whether this is a reference to a policy or to a policy set.
|
|
*
|
|
* @return the reference type, either <code>POLICY_REFERENCE</code>
|
|
* or <code>POLICYSET_REFERENCE</code>
|
|
*/
|
|
public int getReferenceType() {
|
|
return this.policyType;
|
|
}
|
|
|
|
/**
|
|
* Returns the id of this policy. If the policy is invalid or can't be
|
|
* retrieved, then a runtime exception is thrown.
|
|
*
|
|
* @return the policy id
|
|
*
|
|
* @throws ProcessingException if the referenced policy can't be retrieved
|
|
*/
|
|
public URI getId() {
|
|
return resolvePolicy(null).getId();
|
|
}
|
|
|
|
/**
|
|
* Returns the version of this policy. If the policy is invalid or can't
|
|
* be retrieved, then a runtime exception is thrown.
|
|
*
|
|
* @return the policy version
|
|
*
|
|
* @throws ProcessingException if the referenced policy can't be retrieved
|
|
*/
|
|
public String getVersion() {
|
|
return resolvePolicy(null).getVersion();
|
|
}
|
|
|
|
/**
|
|
* Returns the combining algorithm used by this policy. If the policy is
|
|
* invalid or can't be retrieved, then a runtime exception is thrown.
|
|
*
|
|
* @return the combining algorithm
|
|
*
|
|
* @throws ProcessingException if the referenced policy can't be retrieved
|
|
*/
|
|
public CombiningAlgorithm getCombiningAlg() {
|
|
return resolvePolicy(null).getCombiningAlg();
|
|
}
|
|
|
|
/**
|
|
* Returns the given description of this policy or null if there is no
|
|
* description. If the policy is invalid or can't be retrieved, then a
|
|
* runtime exception is thrown.
|
|
*
|
|
* @return the description or null
|
|
*
|
|
* @throws ProcessingException if the referenced policy can't be retrieved
|
|
*/
|
|
public String getDescription() {
|
|
return resolvePolicy(null).getDescription();
|
|
}
|
|
|
|
/**
|
|
* Returns the PolicyIssuer if present or null if it is the trusted issuer.
|
|
*
|
|
* @return the PolicyIssuer or null
|
|
*
|
|
* @throws ProcessingException if the referenced policy can't be retrieved
|
|
*/
|
|
public PolicyIssuer getPolicyIssuer() {
|
|
return resolvePolicy(null).getPolicyIssuer();
|
|
}
|
|
|
|
/**
|
|
* Returns the target for this policy. If the policy is invalid or can't be
|
|
* retrieved, then a runtime exception is thrown.
|
|
*
|
|
* @return the policy's target
|
|
*
|
|
* @throws ProcessingException if the referenced policy can't be retrieved
|
|
*/
|
|
public Target getTarget() {
|
|
return resolvePolicy(null).getTarget();
|
|
}
|
|
|
|
/**
|
|
* Returns the default version for this policy. If the policy is
|
|
* invalid or can't be retrieved, then a runtime exception is thrown.
|
|
*
|
|
* @return the policy's default version
|
|
*
|
|
* @throws ProcessingException if the referenced policy can't be retrieved
|
|
*/
|
|
public String getDefaultVersion() {
|
|
return resolvePolicy(null).getDefaultVersion();
|
|
}
|
|
|
|
/**
|
|
* Returns the child policy nodes under this node in the policy tree. If
|
|
* the policy is invalid or can't be retrieved, then a runtime exception
|
|
* is thrown.
|
|
*
|
|
* @return the <code>List</code> of child policy nodes
|
|
*
|
|
* @throws ProcessingException if the referenced policy can't be retrieved
|
|
*/
|
|
public List<PolicyTreeElement> getChildren() {
|
|
return resolvePolicy(null).getChildren();
|
|
}
|
|
|
|
/**
|
|
* Returns the child policy nodes and their associated parameters. If
|
|
* the policy is invalid or can't be retrieved, then a runtime exception
|
|
* is thrown.
|
|
*
|
|
* @return a <code>List</code> of <code>CombinerElement</code>s
|
|
*
|
|
* @throws ProcessingException if the referenced policy can't be retrieved
|
|
*/
|
|
public List<CombinerElement> getChildElements() {
|
|
return resolvePolicy(null).getChildElements();
|
|
}
|
|
|
|
/**
|
|
* Returns the Set of obligations for this policy, which may be empty if
|
|
* there are no obligations. If the policy is invalid or can't be
|
|
* retrieved, then a runtime exception is thrown.
|
|
*
|
|
* @return the policy's obligations
|
|
*
|
|
* @throws ProcessingException if the referenced policy can't be retrieved
|
|
*/
|
|
public Set<Obligation> getObligations() {
|
|
return resolvePolicy(null).getObligations();
|
|
}
|
|
|
|
/**
|
|
* Returns the meta-data associated with this policy. If the policy is
|
|
* invalid or can't be retrieved, then a runtime exception is thrown.
|
|
* Note that this is the meta-data for the referenced policy, not the
|
|
* meta-data for the parent policy (which is what gets provided to the
|
|
* constructors of this class).
|
|
*
|
|
* @return the policy's meta-data
|
|
*
|
|
* @throws ProcessingException if the referenced policy can't be retrieved
|
|
*/
|
|
public PolicyMetaData getMetaData() {
|
|
return resolvePolicy(null).getMetaData();
|
|
}
|
|
|
|
/**
|
|
* Given the input context sees whether or not the request matches this
|
|
* policy. This must be called by combining algorithms before they
|
|
* evaluate a policy. This is also used in the initial policy finding
|
|
* operation to determine which top-level policies might apply to the
|
|
* request. If the policy is invalid or can't be retrieved, then a
|
|
* runtime exception is thrown.
|
|
*
|
|
* @param context the representation of the request
|
|
*
|
|
* @return the result of trying to match the policy and the request
|
|
*/
|
|
public MatchResult match(EvaluationCtx context) {
|
|
try {
|
|
return resolvePolicy(context).match(context);
|
|
} catch (ProcessingException pe) {
|
|
// this means that we couldn't resolve the policy
|
|
ArrayList<String> code = new ArrayList<String>();
|
|
code.add(Status.STATUS_PROCESSING_ERROR);
|
|
Status status = new Status(code, "couldn't resolve policy ref");
|
|
MatchResult match = new MatchResult(MatchResult.INDETERMINATE,
|
|
status);
|
|
return match;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Private helper method that tried to resolve the policy
|
|
*/
|
|
private AbstractPolicy resolvePolicy(EvaluationCtx context) {
|
|
if ( referencedPolicy != null ) {
|
|
return referencedPolicy;
|
|
}
|
|
// see if this reference was setup with a finder
|
|
if (this.finder == null) {
|
|
logger.warn("PolicyReference with id "
|
|
+ this.reference.toString() + " was queried but "
|
|
+ "was not configured with a PolicyFinder");
|
|
throw new ProcessingException("couldn't find the policy with " +
|
|
"a null finder");
|
|
}
|
|
|
|
PolicyFinderResult pfr = this.finder.findPolicy(context,
|
|
this.reference,
|
|
this.policyType,
|
|
this.constraints,
|
|
this.parentMetaData);
|
|
|
|
if (pfr.notApplicable()) {
|
|
throw new ProcessingException("couldn't resolve the policy");
|
|
}
|
|
if (pfr.indeterminate()) {
|
|
throw new ProcessingException("error resolving the policy");
|
|
}
|
|
referencedPolicy = pfr.getPolicy();
|
|
return referencedPolicy;
|
|
}
|
|
|
|
/**
|
|
* Tries to evaluate the policy by calling the combining algorithm on
|
|
* the given policies or rules. The <code>match</code> method must always
|
|
* be called first, and must always return MATCH, before this method
|
|
* is called.
|
|
*
|
|
* @param context the representation of the request
|
|
*
|
|
* @return the result of evaluation
|
|
*/
|
|
public Result evaluate(EvaluationCtx context) {
|
|
|
|
try {
|
|
resolvePolicy(context);
|
|
} catch (ProcessingException pe) {
|
|
logger.fatal("Evaluation was called for PolicyReference with not resolved Policy: This should not happen as no target can have been matched!");
|
|
List<String> codes = new Vector<String>();
|
|
codes.add(Status.STATUS_PROCESSING_ERROR);
|
|
Status errorStatus = new Status(codes);
|
|
return new Result(Result.DECISION_INDETERMINATE, errorStatus, context);
|
|
}
|
|
|
|
return referencedPolicy.evaluate(context);
|
|
//
|
|
// // if there is no finder, then we return NotApplicable
|
|
// if (this.finder == null) {
|
|
// return new Result(Result.DECISION_NOT_APPLICABLE,
|
|
// context);
|
|
// }
|
|
// PolicyFinderResult pfr = this.finder.findPolicy(this.reference,
|
|
// this.policyType,
|
|
// this.constraints,
|
|
// this.parentMetaData);
|
|
//
|
|
// // if we found nothing, then we return NotApplicable
|
|
// if (pfr.notApplicable()) {
|
|
// return new Result(Result.DECISION_NOT_APPLICABLE, context);
|
|
// }
|
|
// // if there was an error, we return that status data
|
|
// if (pfr.indeterminate()) {
|
|
// return new Result(Result.DECISION_INDETERMINATE,
|
|
// pfr.getStatus(),
|
|
// context);
|
|
// }
|
|
// // we must have found a policy
|
|
// return pfr.getPolicy().evaluate(context);
|
|
}
|
|
|
|
/**
|
|
* Encodes this <code>PolicyReference</code> into its XML representation
|
|
* and writes this encoding to the given <code>OutputStream</code> with
|
|
* no indentation.
|
|
*
|
|
* @param output a stream into which the XML-encoded data is written
|
|
* @param charsetName the character set to use in encoding of strings.
|
|
* This may be null in which case the platform
|
|
* default character set will be used.
|
|
*
|
|
* @throws UnsupportedEncodingException
|
|
*/
|
|
public void encode(OutputStream output, String charsetName)
|
|
throws UnsupportedEncodingException {
|
|
encode(output, charsetName, new Indenter(0));
|
|
}
|
|
|
|
/**
|
|
* Encodes this <code>PolicyReference</code> into its XML representation
|
|
* and writes this encoding to the given <code>OutputStream</code> with
|
|
* indentation.
|
|
*
|
|
* @param output a stream into which the XML-encoded data is written
|
|
* @param charsetName the character set to use in encoding of strings.
|
|
* This may be null in which case the platform
|
|
* default character set will be used.
|
|
* @param indenter an object that creates indentation strings
|
|
*
|
|
* @throws UnsupportedEncodingException
|
|
*/
|
|
public void encode(OutputStream output, String charsetName,
|
|
Indenter indenter)
|
|
throws UnsupportedEncodingException {
|
|
PrintStream out;
|
|
if(charsetName == null) {
|
|
out = new PrintStream(output);
|
|
} else {
|
|
out = new PrintStream(output, false, charsetName);
|
|
}
|
|
String encoded = indenter.makeString();
|
|
|
|
if (this.policyType == POLICY_REFERENCE) {
|
|
out.println(encoded + "<PolicyIdReference xmlns=\""
|
|
+ getMetaData().getXACMLIdentifier() + "\""
|
|
+ encodeConstraints()
|
|
+ ">" + this.reference.toString()
|
|
+ "</PolicyIdReference>");
|
|
} else {
|
|
out.println(encoded + "<PolicySetIdReference"
|
|
+ encodeConstraints() + ">"
|
|
+ this.reference.toString()
|
|
+ "</PolicySetIdReference>");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Private helper method that encodes the variable constraints info. Note
|
|
* that if this is a pre-2.0 policy the constraints are always null, so
|
|
* nothing will be added here.
|
|
*/
|
|
private String encodeConstraints() {
|
|
String str = "";
|
|
VersionConstraints version = getConstraints();
|
|
|
|
String v = version.getVersionConstraint();
|
|
if (v != null) {
|
|
str += " Version=\"" + v + "\"";
|
|
}
|
|
|
|
String e = version.getEarliestConstraint();
|
|
if (e != null) {
|
|
str += " EarliestVersion=\"" + e + "\"";
|
|
}
|
|
|
|
String l = version.getLatestConstraint();
|
|
if (l != null) {
|
|
str += " LatestVersion=\"" + l + "\"";
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
}
|