/* * Copyright 2017-present Open Networking Laboratory * * Licensed 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. */ package org.onosproject.lisp.msg.types.lcaf; import io.netty.buffer.ByteBuf; import org.onlab.util.ByteOperator; import org.onosproject.lisp.msg.exceptions.LispParseError; import org.onosproject.lisp.msg.exceptions.LispReaderException; import org.onosproject.lisp.msg.exceptions.LispWriterException; import org.onosproject.lisp.msg.types.LispAddressReader; import org.onosproject.lisp.msg.types.LispAddressWriter; import org.onosproject.lisp.msg.types.LispAfiAddress; import java.util.Objects; import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkNotNull; /** * Geo Coordinate type LCAF address class. * <p> * Geo Coordinate type is defined in draft-ietf-lisp-lcaf-22 * https://tools.ietf.org/html/draft-ietf-lisp-lcaf-22#page-11 * <p> * <pre> * {@literal * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | AFI = 16387 | Rsvd1 | Flags | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type = 5 | Rsvd2 | Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |N| Latitude Degrees | Minutes | Seconds | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |E| Longitude Degrees | Minutes | Seconds | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Altitude | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | AFI = x | Address ... | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * }</pre> */ public final class LispGeoCoordinateLcafAddress extends LispLcafAddress { private final boolean north; private final short latitudeDegree; private final byte latitudeMinute; private final byte latitudeSecond; private final boolean east; private final short longitudeDegree; private final byte longitudeMinute; private final byte longitudeSecond; private final int altitude; private final LispAfiAddress address; /** * Initializes geo coordinate type LCAF address. * * @param north north flag * @param latitudeDegree latitude degree * @param latitudeMinute latitude minute * @param latitudeSecond latitude second * @param east east flag * @param longitudeDegree longitude degree * @param longitudeMinute longitude minute * @param longitudeSecond longitude second * @param altitude altitude * @param address AFI address */ private LispGeoCoordinateLcafAddress(boolean north, short latitudeDegree, byte latitudeMinute, byte latitudeSecond, boolean east, short longitudeDegree, byte longitudeMinute, byte longitudeSecond, int altitude, LispAfiAddress address) { super(LispCanonicalAddressFormatEnum.GEO_COORDINATE); this.north = north; this.latitudeDegree = latitudeDegree; this.latitudeMinute = latitudeMinute; this.latitudeSecond = latitudeSecond; this.east = east; this.longitudeDegree = longitudeDegree; this.longitudeMinute = longitudeMinute; this.longitudeSecond = longitudeSecond; this.altitude = altitude; this.address = address; } /** * Obtains north flag value. * * @return north flag value */ public boolean isNorth() { return north; } /** * Obtains latitude degree. * * @return latitude degree */ public short getLatitudeDegree() { return latitudeDegree; } /** * Obtains latitude minute. * * @return latitude minute */ public byte getLatitudeMinute() { return latitudeMinute; } /** * Obtains latitude second. * * @return latitude second */ public byte getLatitudeSecond() { return latitudeSecond; } /** * Obtains east flag value. * * @return east flag vlaue */ public boolean isEast() { return east; } /** * Obtains longitude degree. * * @return longitude degree */ public short getLongitudeDegree() { return longitudeDegree; } /** * Obtains longitude minute. * * @return longitude minute */ public byte getLongitudeMinute() { return longitudeMinute; } /** * Obtains longitude second. * * @return longitude second */ public byte getLongitudeSecond() { return longitudeSecond; } /** * Obtains altitude. * * @return altitude */ public int getAltitude() { return altitude; } /** * Obtains AFI address. * * @return AFI address */ public LispAfiAddress getAddress() { return address; } @Override public int hashCode() { return Objects.hash(north, latitudeDegree, latitudeMinute, latitudeSecond, east, longitudeDegree, longitudeMinute, longitudeSecond, altitude, address); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof LispGeoCoordinateLcafAddress) { final LispGeoCoordinateLcafAddress other = (LispGeoCoordinateLcafAddress) obj; return Objects.equals(this.north, other.north) && Objects.equals(this.latitudeDegree, other.latitudeDegree) && Objects.equals(this.latitudeMinute, other.latitudeMinute) && Objects.equals(this.latitudeSecond, other.latitudeSecond) && Objects.equals(this.east, other.east) && Objects.equals(this.longitudeDegree, other.longitudeDegree) && Objects.equals(this.longitudeMinute, other.longitudeMinute) && Objects.equals(this.longitudeSecond, other.longitudeSecond) && Objects.equals(this.altitude, other.altitude) && Objects.equals(this.address, other.address); } return false; } @Override public String toString() { return toStringHelper(this) .add("north", north) .add("latitude degree", latitudeDegree) .add("latitude minute", latitudeMinute) .add("latitude second", latitudeSecond) .add("east", east) .add("longitude degree", longitudeDegree) .add("longitude minute", longitudeMinute) .add("longitude second", longitudeSecond) .add("altitude", altitude) .add("address", address) .toString(); } public static final class GeoCoordinateAddressBuilder extends LcafAddressBuilder<GeoCoordinateAddressBuilder> { private boolean north; private short latitudeDegree; private byte latitudeMinute; private byte latitudeSecond; private boolean east; private short longitudeDegree; private byte longitudeMinute; private byte longitudeSecond; private int altitude; private LispAfiAddress address; /** * Sets north flag value. * * @param north north flag value * @return GeoCoordinateAddressBuilder object */ public GeoCoordinateAddressBuilder withIsNorth(boolean north) { this.north = north; return this; } /** * Sets latitude degree. * * @param latitudeDegree latitude degree * @return GeoCoordinateAddressBuilder object */ public GeoCoordinateAddressBuilder withLatitudeDegree(short latitudeDegree) { this.latitudeDegree = latitudeDegree; return this; } /** * Sets latitude minute. * * @param latitudeMinute latitude minute * @return GeoCoordinateAddressBuilder object */ public GeoCoordinateAddressBuilder withLatitudeMinute(byte latitudeMinute) { this.latitudeMinute = latitudeMinute; return this; } /** * Sets latitude second. * * @param latitudeSecond latitude second * @return GeoCoordinateAddressBuilder object */ public GeoCoordinateAddressBuilder withLatitudeSecond(byte latitudeSecond) { this.latitudeSecond = latitudeSecond; return this; } /** * Sets east flag value. * * @param east east flag * @return GeoCoordinateAddressBuilder object */ public GeoCoordinateAddressBuilder withIsEast(boolean east) { this.east = east; return this; } /** * Sets longitude degree. * * @param longitudeDegree longitude degree * @return GeoCoordinateAddressBuilder object */ public GeoCoordinateAddressBuilder withLongitudeDegree(short longitudeDegree) { this.longitudeDegree = longitudeDegree; return this; } /** * Sets longitude minute. * * @param longitudeMinute longitude minute * @return GeoCoordinateAddressBuilder object */ public GeoCoordinateAddressBuilder withLongitudeMinute(byte longitudeMinute) { this.longitudeMinute = longitudeMinute; return this; } /** * Sets longitude second. * * @param longitudeSecond longitude second * @return GeoCoordinateAddressBuilder object */ public GeoCoordinateAddressBuilder withLongitudeSecond(byte longitudeSecond) { this.longitudeSecond = longitudeSecond; return this; } /** * Sets altitude. * * @param altitude altitude * @return GeoCoordinateAddressBuilder object */ public GeoCoordinateAddressBuilder withAltitude(int altitude) { this.altitude = altitude; return this; } /** * Sets AFI address. * * @param address AFI address * @return GeoCoordinateAddressBuilder object */ public GeoCoordinateAddressBuilder withAddress(LispAfiAddress address) { this.address = address; return this; } /** * Builds LispGeoCoordinateLcafAddress instance. * * @return LispGeoCoordinateLcafAddress instance */ public LispGeoCoordinateLcafAddress build() { checkNotNull(address, "Must specify an AFI address"); return new LispGeoCoordinateLcafAddress(north, latitudeDegree, latitudeMinute, latitudeSecond, east, longitudeDegree, longitudeMinute, longitudeSecond, altitude, address); } } /** * GeoCoordinate LCAF address reader class. */ public static class GeoCoordinateLcafAddressReader implements LispAddressReader<LispGeoCoordinateLcafAddress> { private static final int NORTH_INDEX = 7; private static final int EAST_INDEX = 7; private static final int FLAG_SHIFT = 8; @Override public LispGeoCoordinateLcafAddress readFrom(ByteBuf byteBuf) throws LispParseError, LispReaderException { LispLcafAddress.deserializeCommon(byteBuf); // north flag -> 1 bit byte flagWithLatitude = byteBuf.readByte(); boolean north = ByteOperator.getBit(flagWithLatitude, NORTH_INDEX); // latitude degree -> 15 bits short latitudeFirst = flagWithLatitude; if (north) { latitudeFirst = (short) (flagWithLatitude & 0x7F); } short latitude = (short) ((latitudeFirst << FLAG_SHIFT) + byteBuf.readByte()); // latitude minute -> 8 bits byte latitudeMinute = byteBuf.readByte(); // latitude second -> 8 bits byte latitudeSecond = byteBuf.readByte(); // east flag -> 1 bit byte flagWithLongitude = byteBuf.readByte(); boolean east = ByteOperator.getBit(flagWithLongitude, EAST_INDEX); // longitude degree -> 15 bits short longitudeFirst = flagWithLongitude; if (east) { longitudeFirst = (short) (flagWithLongitude & 0x7F); } short longitude = (short) ((longitudeFirst << FLAG_SHIFT) + byteBuf.readByte()); // longitude minute -> 8 bits byte longitudeMinute = byteBuf.readByte(); // longitude second -> 8 bits byte longitudeSecond = byteBuf.readByte(); // altitude -> 32 bits int altitude = byteBuf.readInt(); LispAfiAddress address = new AfiAddressReader().readFrom(byteBuf); return new GeoCoordinateAddressBuilder() .withIsNorth(north) .withLatitudeDegree(latitude) .withLatitudeMinute(latitudeMinute) .withLatitudeSecond(latitudeSecond) .withIsEast(east) .withLongitudeDegree(longitude) .withLongitudeMinute(longitudeMinute) .withLongitudeSecond(longitudeSecond) .withAltitude(altitude) .withAddress(address) .build(); } } /** * GeoCoordinate LCAF address writer class. */ public static class GeoCoordinateLcafAddressWriter implements LispAddressWriter<LispGeoCoordinateLcafAddress> { private static final int NORTH_SHIFT_BIT = 15; private static final int EAST_SHIFT_BIT = 15; private static final int ENABLE_BIT = 1; private static final int DISABLE_BIT = 0; @Override public void writeTo(ByteBuf byteBuf, LispGeoCoordinateLcafAddress address) throws LispWriterException { int lcafIndex = byteBuf.writerIndex(); LispLcafAddress.serializeCommon(byteBuf, address); // north flag + latitude degree short north = DISABLE_BIT; if (address.isNorth()) { north = (short) (ENABLE_BIT << NORTH_SHIFT_BIT); } byteBuf.writeShort(north + address.latitudeDegree); // latitude minute byteBuf.writeByte(address.latitudeMinute); // latitude second byteBuf.writeByte(address.latitudeSecond); // east flag + longitude degree short east = DISABLE_BIT; if (address.isEast()) { east = (short) (ENABLE_BIT << EAST_SHIFT_BIT); } byteBuf.writeShort(east + address.longitudeDegree); // longitude minute byteBuf.writeByte(address.longitudeMinute); // longitude second byteBuf.writeByte(address.longitudeSecond); // altitude byteBuf.writeInt(address.altitude); // address AfiAddressWriter writer = new AfiAddressWriter(); writer.writeTo(byteBuf, address.getAddress()); LispLcafAddress.updateLength(lcafIndex, byteBuf); } } }