Package mireka.address.parser

Source Code of mireka.address.parser.Ipv6Parser$AddressContextualAnalyzer

package mireka.address.parser;

import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import mireka.address.parser.Ipv6Token.Kind;
import mireka.address.parser.base.AST;
import mireka.address.parser.base.CharScanner;
import mireka.address.parser.base.Spelling;
import mireka.address.parser.base.Terminal;

/**
* Ipv6Parser parses an IPv6 address literal and convert it to an
* {@link Inet6Address}. For example:
* <ul>
* <li>2001:db8:0:0:0:0:0:0
* <li>2001:db8::
* <li>2001:db8::1
* <li>::1
* <li>2001:db8::192.0.2.0
* </ul>
* <p>
* Grammar:
*
* <pre>
* IPv6         := [NUM_SEQENCE] ENDING
* NUM_SEQENCE  := NUM *(: NUM)
* NUM          := 1*4HEXDIG
* ENDING       := :: [NUM_SEQENCE [. IPv4Rest]]
*               | . IPv4Rest
*               | E
* IPv4Rest     := NUM . NUM . NUM
* </pre>
*/
public class Ipv6Parser {
    private Ipv6Scanner scanner;
    private Ipv6Token currentToken;
    private Spelling spelling = new Spelling();

    public Ipv6Parser(String address) throws ParseException {
        this(new CharScanner(address));
    }

    public Ipv6Parser(CharScanner charScanner) throws ParseException {
        this.scanner = new Ipv6Scanner(charScanner);
        currentToken = scanner.scan();
    }

    public Ipv6 parseLeft() throws ParseException {
        Ipv6 ipv6AST = parseIpv6();
        new AddressContextualAnalyzer(ipv6AST).decorate();
        scanner.finish(currentToken);
        return ipv6AST;
    }

    public Ipv6 parse() throws ParseException {
        Ipv6 ipv6AST = parseIpv6();
        new AddressContextualAnalyzer(ipv6AST).decorate();
        if (currentToken.kind != Kind.EOF)
            throw currentToken.otherSyntaxException("Superfluous characters "
                    + "after IPv6 address: {0}.");
        return ipv6AST;
    }

    private Ipv6 parseIpv6() throws ParseException {
        int position = currentToken.position;
        spelling.start();
        NumSequence numSequenceAST = null;
        if (currentToken.kind == Kind.NUM) {
            numSequenceAST = parseNumSequence();
        }
        Ending ending = parseEnding();
        return new Ipv6(position, spelling.finish(), numSequenceAST, ending);
    }

    private NumSequence parseNumSequence() throws ParseException {
        int position = currentToken.position;
        List<Num> numbers = new ArrayList<Num>();
        numbers.add(parseNum());
        while (currentToken.kind == Kind.COLON) {
            acceptIt();
            numbers.add(parseNum());
        }
        return new NumSequence(position, numbers);
    }

    private Num parseNum() throws ParseException {
        if (currentToken.kind == Kind.NUM) {
            Num numAST = new Num(currentToken);
            acceptIt();
            return numAST;
        } else {
            throw currentToken.syntaxException("hex digits");
        }
    }

    private Ending parseEnding() throws ParseException {
        int position = currentToken.position;
        RestOfIpv4 restOfIpv4AST = null;
        switch (currentToken.kind) {
        case DOUBLE_COLON:
            acceptIt();
            NumSequence numSequenceAST = null;

            if (currentToken.kind == Kind.NUM) {
                numSequenceAST = parseNumSequence();
                if (currentToken.kind == Kind.DOT) {
                    acceptIt();
                    restOfIpv4AST = parseRestOfIpv4();
                }
            }
            return new DoubleColonEnding(position, numSequenceAST,
                    restOfIpv4AST);
        case DOT:
            acceptIt();
            restOfIpv4AST = parseRestOfIpv4();
            return new Ipv4Ending(position, restOfIpv4AST);
        case OTHER:
        case EOF:
            return new EmptyEnding(position);
        default:
            throw currentToken
                    .syntaxException("'.', '::' or end of IPv6 literal");
        }
    }

    private RestOfIpv4 parseRestOfIpv4() throws ParseException {
        int position = currentToken.position;
        Num dec2AST = parseNum();
        accept(Kind.DOT);
        Num dec3AST = parseNum();
        accept(Kind.DOT);
        Num dec4AST = parseNum();
        return new RestOfIpv4(position, dec2AST, dec3AST, dec4AST);
    }

    private void accept(Kind kind) throws ParseException {
        if (currentToken.kind == kind)
            acceptIt();
        else
            throw currentToken.syntaxException(kind);
    }

    private void acceptIt() throws ParseException {
        spelling.append(currentToken.spelling);
        currentToken = scanner.scan();
    }

    /**
     * AddressContextualAnalyzer does additional checks on the address which are
     * not included in the grammar and extracts the IPv6 address bytes from the
     * abstract syntax tree.
     */
    private static class AddressContextualAnalyzer {
        final Ipv6 ipv6AST;
        LinkedList<Num> leftNumbers = new LinkedList<Num>();
        LinkedList<Num> rightNumbers = new LinkedList<Num>();
        LinkedList<Num> ipv4Numbers = new LinkedList<Num>();
        boolean hasDoubleColon = false;

        public AddressContextualAnalyzer(Ipv6 ipv6ast) {
            super();
            ipv6AST = ipv6ast;
        }

        /**
         * Traverses the abstract tree and decorates the Ipv6 node with the
         * evaluates address in the form of a byte array and an
         * {@link Inet6Address} object.
         */
        public void decorate() throws ParseException {
            collectNumbers(ipv6AST);
            checkLength();
            byte[] addressBytes = convertToBytes();

            ipv6AST.addressBytes = addressBytes;
            try {
                ipv6AST.address = InetAddress.getByAddress(addressBytes);
            } catch (UnknownHostException e) {
                // this could only happen if the length of the byte array were
                // invalid (not 16), which is impossible.
                throw new RuntimeException("Assertion failed", e);
            }
        }

        private void collectNumbers(Ipv6 ipv6AST) throws ParseException {
            if (ipv6AST.leftNumSequence != null)
                leftNumbers.addAll(ipv6AST.leftNumSequence.numbers);
            if (ipv6AST.ending instanceof DoubleColonEnding) {
                DoubleColonEnding ending = (DoubleColonEnding) ipv6AST.ending;
                hasDoubleColon = true;
                if (ending.rightNumSequence != null) {
                    rightNumbers.addAll(ending.rightNumSequence.numbers);
                    if (ending.restOfIpv4 != null) {
                        ipv4Numbers.add(rightNumbers.removeLast());
                        ipv4Numbers.add(ending.restOfIpv4.dec2);
                        ipv4Numbers.add(ending.restOfIpv4.dec3);
                        ipv4Numbers.add(ending.restOfIpv4.dec4);
                    }
                }
            } else if (ipv6AST.ending instanceof Ipv4Ending) {
                Ipv4Ending ending = (Ipv4Ending) ipv6AST.ending;
                if (ipv6AST.leftNumSequence == null)
                    throw ending.syntaxException("Dot must follow a decimal "
                            + "number in the IPv4 part of an IPv6 address.");
                ipv4Numbers.add(leftNumbers.removeLast());
                ipv4Numbers.add(ending.restOfIpv4.dec2);
                ipv4Numbers.add(ending.restOfIpv4.dec3);
                ipv4Numbers.add(ending.restOfIpv4.dec4);
            } else if (ipv6AST.ending instanceof EmptyEnding) {
                // nothing to do
            }
        }

        private void checkLength() throws ParseException {
            // bytes without the :: placeholder
            int countOfExplicitlySpecifiedBytes =
                    leftNumbers.size() * 2 + rightNumbers.size() * 2
                            + ipv4Numbers.size();
            int maxExplicitlySpecifiedBytes = hasDoubleColon ? 14 : 16;
            if (countOfExplicitlySpecifiedBytes > maxExplicitlySpecifiedBytes)
                throw ipv6AST.syntaxException("IPv6 address literal specifies "
                        + "more than 16 bytes.");
            if (!hasDoubleColon && countOfExplicitlySpecifiedBytes < 16)
                throw ipv6AST.syntaxException("IPv6 address literal specifies "
                        + "less than 16 bytes.");
        }

        private byte[] convertToBytes() throws ParseException {
            byte[] result = new byte[16];
            int pos = 0;
            for (Num hex : leftNumbers) {
                int doubleByte = evaluateHexDoubleByte(hex);
                result[pos++] = (byte) (doubleByte >> 8);
                result[pos++] = (byte) (doubleByte & 0xFF);
            }
            pos = 15;
            for (Iterator<Num> it = ipv4Numbers.descendingIterator(); it
                    .hasNext();) {
                Num dec = it.next();
                result[pos--] = evaluateDecByte(dec);
            }
            for (Iterator<Num> it = rightNumbers.descendingIterator(); it
                    .hasNext();) {
                Num hex = it.next();
                int doubleByte = evaluateHexDoubleByte(hex);
                result[pos--] = (byte) (doubleByte & 0xFF);
                result[pos--] = (byte) (doubleByte >> 8);
            }
            return result;
        }

        private int evaluateHexDoubleByte(Num hex) {
            return Integer.parseInt(hex.spelling, 16);
        }

        private byte evaluateDecByte(Num dec) throws ParseException {
            try {
                int result = Integer.parseInt(dec.spelling);
                if (result > 255)
                    throw dec.syntaxException("Byte value must be lower "
                            + "than or equal with 255 in the IPv4 "
                            + "compatible part of an IPv6 address.");
                return (byte) result;
            } catch (NumberFormatException e) {
                throw dec.syntaxException("The IPv4 compatible part of "
                        + "an IPv6 address must consists of decimal "
                        + "and not hex digits.");
            }
        }
    }

    public static class Ipv6 extends AST {
        public String spelling;
        private NumSequence leftNumSequence;
        private Ending ending;
        public byte[] addressBytes;
        public InetAddress address;

        private Ipv6(int position, String spelling,
                NumSequence leftNumSequence, Ending ending) {
            super(position);
            this.spelling = spelling;
            this.leftNumSequence = leftNumSequence;
            this.ending = ending;
        }

    }

    private static class NumSequence extends AST {

        public List<Num> numbers;

        public NumSequence(int position, List<Num> numbers) {
            super(position);
            this.numbers = numbers;
        }
    }

    private static class Num extends Terminal {

        public Num(Ipv6Token token) {
            super(token.position, token.spelling);
        }

    }

    private static class Ending extends AST {

        public Ending(int position) {
            super(position);
        }

    }

    private static class DoubleColonEnding extends Ending {
        public NumSequence rightNumSequence;
        public RestOfIpv4 restOfIpv4;

        public DoubleColonEnding(int position, NumSequence rightNumSequence,
                RestOfIpv4 restOfIpv4) {
            super(position);
            this.rightNumSequence = rightNumSequence;
            this.restOfIpv4 = restOfIpv4;
        }

    }

    private static class RestOfIpv4 extends AST {
        public Num dec2;
        public Num dec3;
        public Num dec4;

        public RestOfIpv4(int position, Num dec2, Num dec3, Num dec4) {
            super(position);
            this.dec2 = dec2;
            this.dec3 = dec3;
            this.dec4 = dec4;
        }
    }

    public static class Ipv4Ending extends Ending {
        public RestOfIpv4 restOfIpv4;

        public Ipv4Ending(int position, RestOfIpv4 restOfIpv4) {
            super(position);
            this.restOfIpv4 = restOfIpv4;
        }

    }

    public static class EmptyEnding extends Ending {

        public EmptyEnding(int position) {
            super(position);
        }

    }
}
TOP

Related Classes of mireka.address.parser.Ipv6Parser$AddressContextualAnalyzer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.