blob: fc58e3e78813de05fbce1ee7f4d13877852abef4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2017 Genuitec, LLC and others.
*
* This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License 2.0 which accompanies this distribution, and is
* available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Genuitec, LLC - initial API and implementation
* IBM Corporation - ongoing development
* EclipseSource - ongoing development
******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata;
import java.math.BigInteger;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.eclipse.equinox.p2.metadata.ILicense;
/**
* The <code>License</code> class represents a software license. A license has required body text
* which may be the full text or an annotation. An optional URL field can be specified
* which links to full text. Licenses can be easily compared using their digests.
*/
public class License implements ILicense {
/**
* The <code>body</code> contains the descriptive text for the license. This may
* be a summary for a full license specified in a URL.
*/
private final String body;
/**
* The <code>location</code> is the URL of the license.
*/
private URI location;
/**
* The <code>digest</code> is the cached message digest of the normalized body
*/
private String digest;
/**
* Creates a new license object which is identified by users using the <code>body</code> field.
* The body should contain either the full text of the license or an summary for a license
* fully specified in the given location.
*
* @param location the location of a document containing the full license, or <code>null</code>
* @param body the license body, cannot be <code>null</code>
* @throws IllegalArgumentException when the <code>body</code> is <code>null</code>
*/
public License(URI location, String body, String uuid) {
if (body == null)
throw new IllegalArgumentException("body cannot be null"); //$NON-NLS-1$
this.body = body;
this.location = location;
this.digest = uuid;
}
/**
* Returns the location of a document containing the full license.
*
* @return the location of the license document, or <code>null</code>
*/
@Override
public URI getLocation() {
return location;
}
/**
* Returns the license body.
* @return the license body, never <code>null</code>
*/
@Override
public String getBody() {
return body;
}
/**
* Returns the message digest of the license body. The digest is calculated on a normalized
* version of the license where all whitespace has been reduced to one space.
* @return the message digest as a <code>BigInteger</code>, never <code>null</code>
*/
@Override
public synchronized String getUUID() {
if (digest == null)
digest = calculateLicenseDigest().toString(16);
return digest;
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == null)
return false;
if (obj instanceof ILicense) {
ILicense other = (ILicense) obj;
if (other.getUUID().equals(getUUID()))
return true;
}
return false;
}
@Override
public int hashCode() {
return getUUID().hashCode();
}
private BigInteger calculateLicenseDigest() {
String message = normalize(getBody());
try {
MessageDigest algorithm = MessageDigest.getInstance("MD5"); //$NON-NLS-1$
algorithm.reset();
algorithm.update(message.getBytes(StandardCharsets.UTF_8));
byte[] digestBytes = algorithm.digest();
return new BigInteger(1, digestBytes);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
/**
* Replace all sequences of whitespace with a single whitespace character.
*/
private String normalize(String license) {
String text = license.trim();
StringBuilder result = new StringBuilder();
int length = text.length();
for (int i = 0; i < length; i++) {
char c = text.charAt(i);
boolean foundWhitespace = false;
while (Character.isWhitespace(c) && i < length) {
foundWhitespace = true;
c = text.charAt(++i);
}
if (foundWhitespace)
result.append(' ');
if (i < length)
result.append(c);
}
return result.toString();
}
}