package mireka.address.parser;
import static mireka.address.parser.CharClasses.*;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import mireka.address.parser.base.AST;
import mireka.address.parser.base.CharParser;
import mireka.address.parser.base.CharScanner;
public class Ipv4Parser extends CharParser {
public Ipv4Parser(CharScanner charScanner) {
super(charScanner);
}
public Ipv4 parse() throws ParseException {
Ipv4 ipv4AST = parseIpv4();
new AddressContextualAnalyzer(ipv4AST).decorate();
if (currentToken.ch != -1)
throw currentToken.otherSyntaxException("Superfluous characters "
+ "after IPv4 address: {0}.");
return ipv4AST;
}
public Ipv4 parseLeft() throws ParseException {
Ipv4 ipv4AST = parseIpv4();
new AddressContextualAnalyzer(ipv4AST).decorate();
scanner.pushBack(currentToken);
return ipv4AST;
}
private Ipv4 parseIpv4() throws ParseException {
pushPosition();
pushSpelling();
List<Snum> snumASTs = new ArrayList<Snum>();
snumASTs.add(parseSnum());
for (int i = 0; i < 3; i++) {
accept('.');
snumASTs.add(parseSnum());
}
return new Ipv4(popPosition(), popSpelling(), snumASTs);
}
private Snum parseSnum() throws ParseException {
pushPosition();
pushSpelling();
accept(DIGIT);
if (DIGIT.isSatisfiedBy(currentToken.ch))
accept(DIGIT);
if (DIGIT.isSatisfiedBy(currentToken.ch))
accept(DIGIT);
return new Snum(popPosition(), popSpelling());
}
public static class Ipv4 extends AST {
public String spelling;
private List<Snum> snums;
public byte[] addressBytes;
public InetAddress address;
public Ipv4(int position, String spelling, List<Snum> snums) {
super(position);
this.spelling = spelling;
this.snums = snums;
}
}
private static class Snum extends AST {
private String spelling;
public Snum(int position, String spelling) {
super(position);
this.spelling = spelling;
}
}
/**
* 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 Ipv4 ipv4AST;
public AddressContextualAnalyzer(Ipv4 ipv4ast) {
ipv4AST = ipv4ast;
}
/**
* 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 {
byte[] addressBytes = convertToBytes();
ipv4AST.addressBytes = addressBytes;
try {
ipv4AST.address = InetAddress.getByAddress(addressBytes);
} catch (UnknownHostException e) {
// this could only happen if the length of the byte array were
// invalid (not 4), which is impossible.
throw new RuntimeException("Assertion failed", e);
}
}
private byte[] convertToBytes() throws ParseException {
byte[] result = new byte[4];
int pos = 0;
for (Snum dec : ipv4AST.snums) {
result[pos++] = evaluateDecByte(dec);
}
return result;
}
private byte evaluateDecByte(Snum 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 new RuntimeException("Assertion failed");
}
}
}
}