/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** A class to create and recreate UUIDs.
* http://www.opengroup.org/onlinepubs/009629399/apdxa.htm
*/
package org.apache.jena.shared.uuid ;
import java.util.Locale ;
import java.util.UUID ;
import org.apache.jena.atlas.lib.BitsLong ;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
// TO DO
// + Comments and renaming.
// ? Move to/from string code here (string <=> pair of longs).
// OK but unparse code makes explicit what goes where in the structures
// parse/unparseV4 is the generic code.
// UUID and factory
public abstract class JenaUUID
{
static final int HEX = 16 ;
static final int Var_NCS = 0 ;
static final int Var_Std = 2 ; // Same as DCE
static final int Var_DCE = 2 ;
static final int Var_MS_GUID = 6 ;
static final int Var_Reserved = 7 ;
abstract public int getVersion() ;
abstract public int getVariant() ;
abstract public long getMostSignificantBits() ;
abstract public long getLeastSignificantBits() ;
protected int _getVersion(long mostSigBits, long leastSigBits) {
// int variant = (int)((UUID_V1_Gen.maskVariant & leastSigBits)>>>62) ;
// int version = (int)((UUID_V1_Gen.maskVersion & mostSigBits)>>>12) ;
int version = (int)BitsLong.unpack(mostSigBits, 12, 16) ;
return version ;
}
protected int _getVariant(long mostSigBits, long leastSigBits) {
int variant = (int)BitsLong.unpack(leastSigBits, 62, 64) ;
return variant ;
}
protected JenaUUID() {}
/** Format as a string - no URI scheme **/
public String asString() {
return toString() ;
}
/** Format as a URI - that is uuid:ABCD */
public String asURI() {
return "uuid:" + toString() ;
}
/** Format as a URN - that is urn:uuid:ABCD */
public String asURN() {
return "urn:uuid:" + toString() ;
}
/** Return a {@link java.util.UUID} for this Jena-generated UUID */
public UUID asUUID() {
return new UUID(getMostSignificantBits(), getLeastSignificantBits()) ;
}
@Override
public String toString() {
return toString(this) ;
}
// Time low - which includes the incremental count.
@Override
public int hashCode() { return (int) BitsLong.unpack(getMostSignificantBits(), 32, 64) ; }
@Override
public boolean equals(Object other)
{
if ( this == other ) return true ;
if ( other == null ) return false ;
if ( ! ( other instanceof JenaUUID ) )
return false ;
JenaUUID x = (JenaUUID)other ;
return this.getMostSignificantBits() == x.getMostSignificantBits() &&
this.getLeastSignificantBits() == x.getLeastSignificantBits() ;
}
// ----------------------------------------------------
// Factory
static UUIDFactory factory = new UUID_V1_Gen() ;
public static void setFactory(UUIDFactory factory) {
JenaUUID.factory = factory ;
}
public static UUIDFactory getFactory() {
return factory ;
}
/** Create a UUID */
public static JenaUUID generate() {
return factory.generate() ;
}
public static void reset() {
factory.reset() ;
}
/** The nil UUID */
public static JenaUUID nil() {
return UUID_nil.getNil() ;
}
public static String strNil() {
return UUID_nil.getNilString() ;
}
public boolean isNil() {
return this.equals(nil()) ;
} // Or this == UUID_nil.nil because it's a singleton.
/** Recreate a UUID from string */
public static JenaUUID parse(String s) {
if ( s.equals(strNil()) )
return nil() ;
// Canonical: this works in conjunction with .equals
s = s.toLowerCase(Locale.ENGLISH) ;
if ( s.startsWith("urn:") )
s = s.substring(4) ;
if ( s.startsWith("uuid:") )
s = s.substring(5) ;
if ( s.length() != 36 )
throw new UUIDFormatException("UUID string is not 36 chars long: it's " + s.length() + " [" + s + "]") ;
if ( s.charAt(8) != '-' || s.charAt(13) != '-' || s.charAt(18) != '-' || s.charAt(23) != '-' )
throw new UUIDFormatException("String does not have dashes in the right places: " + s) ;
// The UUID broken up into parts.
// 00000000-0000-0000-0000-000000000000
// ^ ^ ^ ^ ^
// Byte: 0 4 6 8 10
// Char: 0 9 14 19 24 including hyphens
int x = (int)BitsLong.unpack(s, 19, 23) ;
int variant = (x >>> 14) ;
int version = (int)BitsLong.unpack(s, 14, 15) ;
if ( variant == Var_Std ) {
switch (version) {
case UUID_V1.version :
return UUID_V1_Gen.parse$(s) ;
case UUID_V4.version :
return UUID_V4_Gen.parse$(s) ;
}
LoggerFactory.getLogger(JenaUUID.class).warn(s + " : Unsupported version: " + version) ;
throw new UnsupportedOperationException("String specifies unsupported UUID version: " + version) ;
}
Logger log = LoggerFactory.getLogger(JenaUUID.class) ;
switch (variant) {
case Var_NCS : // NCS
log.warn(s + " : Oh look! An NCS UUID ID. Call the museum.") ;
break ;
case Var_DCE : // DCE - should have been caught earlier.
log.warn(s + " : Oh look! A DCE UUID ID - but we should have already handled this") ;
break ;
case Var_MS_GUID :
log.warn(s + " : Microsoft UUID ID.") ;
break ;
case Var_Reserved :
log.warn(s + " : Reserved variant") ;
break ;
default :
log.warn(s + " : Unknown variant: " + variant) ;
break ;
}
throw new UnsupportedOperationException("String specifies unsupported UUID variant: " + variant) ;
}
public static String toString(JenaUUID uuid) {
return toString(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()) ;
}
/** Format using two longs - assumed valid for an UUID of some kind */
public static String toString(long mostSignificantBits, long leastSignificantBits) {
StringBuffer sb = new StringBuffer(36) ;
JenaUUID.toHex(sb, BitsLong.unpack(mostSignificantBits, 32, 64), 4) ;
sb.append('-') ;
JenaUUID.toHex(sb, BitsLong.unpack(mostSignificantBits, 16, 32), 2) ;
sb.append('-') ;
JenaUUID.toHex(sb, BitsLong.unpack(mostSignificantBits, 0, 16), 2) ;
sb.append('-') ;
JenaUUID.toHex(sb, BitsLong.unpack(leastSignificantBits, 48, 64), 2) ;
sb.append('-') ;
JenaUUID.toHex(sb, BitsLong.unpack(leastSignificantBits, 0, 48), 6) ;
return sb.toString() ;
}
// ----------------------------------------------------
// Worker functions
static void toHex(StringBuffer sBuff, long value, int lenBytes) {
// Insert in high-low order, by nibble
for (int i = 2 * lenBytes - 1; i >= 0; i--) {
int shift = 4 * i ;
int x = (int)(value >>> shift & 0xF) ;
sBuff.append(Character.forDigit(x, 16)) ;
}
}
static public class UUIDFormatException extends RuntimeException
{
public UUIDFormatException() {
super() ;
}
public UUIDFormatException(String msg) {
super(msg) ;
}
}
}