package edu.harvard.iq.dataverse.authorization.groups.impl.ipaddress.ip; import java.util.Arrays; /** * * @author michael */ public class IPv6Address extends IpAddress implements Comparable<IPv6Address> { public static IPv6Address valueOf( String in ) { if ( in.contains("%") ) { // remove network interface name, if present. in = in.split("%")[0]; } if ( in.contains(".") ) { return valueOfMapped( in ); } if ( in.contains("::") ) { // expand the :: abbreviation int existingFields = 0; for ( String cmp : in.split(":") ) { if ( ! cmp.trim().isEmpty() ) { existingFields++; } } int missingFieldCount = 8-existingFields; StringBuilder sb = new StringBuilder( in.startsWith("::") ? "" : ":" ); for ( int i=0; i<missingFieldCount; i++ ) { sb.append("0:"); } if ( in.endsWith("::") ) { sb.setLength( sb.length()-1 ); } in = in.replace( "::", sb.toString() ); } // Invariant: in is expanded (no "::" abbreviation) String[] comps = in.split(":", -1); if ( comps.length != 8 ) { throw new IllegalArgumentException("IPv6 requires 8 words (or the usage of the :: abbreviation)"); } int[] words = new int[8]; // Invariant: in is of the form "n:n:n:n:n:n:n:n", where n is hopefully a hex number. int wordIdx = 0; for ( String comp : comps ) { try { words[wordIdx++] = Integer.parseInt(comp, 16); } catch ( NumberFormatException nfe ) { throw new IllegalArgumentException("Numbers in IPv6 addresses should be in hexadecimal notation.", nfe); } } return new IPv6Address( words, false ); } public static IPv6Address valueOfMapped( String in ) { // Split parts int lastColon = in.lastIndexOf(":"); String ipv4Part = in.substring(lastColon+1); String ipv6Part = in.substring(0, lastColon+1) + "0:0"; // Parse short[] ipv4bytes = IPv4Address.valueOf(ipv4Part).bytes; int[] ipv6words = IPv6Address.valueOf(ipv6Part).words; // merge ipv6words[6]=(((int)ipv4bytes[0])<<8)+ipv4bytes[1]; ipv6words[7]=(((int)ipv4bytes[2])<<8)+ipv4bytes[3]; return new IPv6Address(ipv6words); } private final int[] words; /** * Constructor that does not copy the int array - but can be used * only from within this class. Especially made for the {@link valueOf} method. * @param words * @param dummy */ private IPv6Address( int[] words, boolean dummy ) { this.words = words; } public IPv6Address(int[] words) { if ( words.length != 8 ) throw new IllegalArgumentException("IPv6 address requires exactly 8 ints. Consider using the valueOf method to support abbreviations"); this.words = Arrays.copyOf(words, words.length); } public IPv6Address( long[] longs ) { words = new int[]{ (int)(longs[0] >>> 32), (int)(longs[0] & 0xffffffffl), (int)(longs[1] >>> 32), (int)(longs[1] & 0xffffffffl), (int)(longs[2] >>> 32), (int)(longs[2] & 0xffffffffl), (int)(longs[3] >>> 32), (int)(longs[3] & 0xffffffffl) }; } public IPv6Address( int w1, int w2, int w3, int w4, int w5, int w6, int w7, int w8 ) { words = new int[]{w1, w2, w3, w4, w5, w6, w7, w8}; } public int get(int idx) { return words[idx]; } public long[] toLongArray() { long[] retVal = new long[4]; for ( int i=0; i<4; i++ ) { retVal[i] = words[2*i]; retVal[i] = (retVal[i]<<32); retVal[i] = retVal[i] + words[2*i+1]; } return retVal; } @Override public int hashCode() { int hash = 7; hash = 89 * hash + Arrays.hashCode(this.words); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if ( ! (obj instanceof IPv6Address) ) { return false; } final IPv6Address other = (IPv6Address) obj; return Arrays.equals(this.words, other.words); } @Override public boolean isLocalhost() { return Arrays.equals(words, new int[]{0,0,0,0,0,0,0,1} ); } @Override public String toString() { StringBuilder sb = new StringBuilder(); for ( int i=0; i<words.length; i++ ) { sb.append( Integer.toString(words[i], 16) ) .append( i<7 ? ":" : "" ); } return sb.toString(); } @Override public int compareTo(IPv6Address o) { for ( int i=0; i<8; i++ ) { if ( get(i) != o.get(i) ) { return get(i)-o.get(i); } } return 0; } }