NekoX/TMessagesProj/src/main/java/org/telegram/ui/Components/SvgHelper.java

1304 lines
44 KiB
Java

/*
Modified by Nikolai Kudashov
Changed drawing to Bitmap instead of Picture
Added styles support
Fixed some float parsing issues
Removed gradients support
Copyright Larva Labs, LLC
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.telegram.ui.Components;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import org.telegram.messenger.FileLog;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
public class SvgHelper {
public static Bitmap getBitmap(File file, int width, int height, boolean white) {
try (FileInputStream stream = new FileInputStream(file)) {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
SVGHandler handler = new SVGHandler(width, height, white);
xr.setContentHandler(handler);
xr.parse(new InputSource(stream));
return handler.getBitmap();
} catch (Exception e) {
FileLog.e(e);
throw null;
}
}
private static NumberParse parseNumbers(String s) {
int n = s.length();
int p = 0;
ArrayList<Float> numbers = new ArrayList<>();
boolean skipChar = false;
for (int i = 1; i < n; i++) {
if (skipChar) {
skipChar = false;
continue;
}
char c = s.charAt(i);
switch (c) {
case 'M':
case 'm':
case 'Z':
case 'z':
case 'L':
case 'l':
case 'H':
case 'h':
case 'V':
case 'v':
case 'C':
case 'c':
case 'S':
case 's':
case 'Q':
case 'q':
case 'T':
case 't':
case 'a':
case 'A':
case ')': {
String str = s.substring(p, i);
if (str.trim().length() > 0) {
Float f = Float.parseFloat(str);
numbers.add(f);
}
p = i;
return new NumberParse(numbers, p);
}
case '\n':
case '\t':
case ' ':
case ',':
case '-': {
if (c == '-' && s.charAt(i - 1) == 'e') {
continue;
}
String str = s.substring(p, i);
if (str.trim().length() > 0) {
Float f = Float.parseFloat(str);
numbers.add(f);
if (c == '-') {
p = i;
} else {
p = i + 1;
skipChar = true;
}
} else {
p++;
}
break;
}
}
}
String last = s.substring(p);
if (last.length() > 0) {
try {
numbers.add(Float.parseFloat(last));
} catch (NumberFormatException ignore) {
}
p = s.length();
}
return new NumberParse(numbers, p);
}
private static Matrix parseTransform(String s) {
if (s.startsWith("matrix(")) {
NumberParse np = parseNumbers(s.substring("matrix(".length()));
if (np.numbers.size() == 6) {
Matrix matrix = new Matrix();
matrix.setValues(new float[]{
np.numbers.get(0), np.numbers.get(2), np.numbers.get(4),
np.numbers.get(1), np.numbers.get(3), np.numbers.get(5),
0, 0, 1,
});
return matrix;
}
} else if (s.startsWith("translate(")) {
NumberParse np = parseNumbers(s.substring("translate(".length()));
if (np.numbers.size() > 0) {
float tx = np.numbers.get(0);
float ty = 0;
if (np.numbers.size() > 1) {
ty = np.numbers.get(1);
}
Matrix matrix = new Matrix();
matrix.postTranslate(tx, ty);
return matrix;
}
} else if (s.startsWith("scale(")) {
NumberParse np = parseNumbers(s.substring("scale(".length()));
if (np.numbers.size() > 0) {
float sx = np.numbers.get(0);
float sy = 0;
if (np.numbers.size() > 1) {
sy = np.numbers.get(1);
}
Matrix matrix = new Matrix();
matrix.postScale(sx, sy);
return matrix;
}
} else if (s.startsWith("skewX(")) {
NumberParse np = parseNumbers(s.substring("skewX(".length()));
if (np.numbers.size() > 0) {
float angle = np.numbers.get(0);
Matrix matrix = new Matrix();
matrix.postSkew((float) Math.tan(angle), 0);
return matrix;
}
} else if (s.startsWith("skewY(")) {
NumberParse np = parseNumbers(s.substring("skewY(".length()));
if (np.numbers.size() > 0) {
float angle = np.numbers.get(0);
Matrix matrix = new Matrix();
matrix.postSkew(0, (float) Math.tan(angle));
return matrix;
}
} else if (s.startsWith("rotate(")) {
NumberParse np = parseNumbers(s.substring("rotate(".length()));
if (np.numbers.size() > 0) {
float angle = np.numbers.get(0);
float cx = 0;
float cy = 0;
if (np.numbers.size() > 2) {
cx = np.numbers.get(1);
cy = np.numbers.get(2);
}
Matrix matrix = new Matrix();
matrix.postTranslate(cx, cy);
matrix.postRotate(angle);
matrix.postTranslate(-cx, -cy);
return matrix;
}
}
return null;
}
private static Path doPath(String s) {
int n = s.length();
ParserHelper ph = new ParserHelper(s, 0);
ph.skipWhitespace();
Path p = new Path();
float lastX = 0;
float lastY = 0;
float lastX1 = 0;
float lastY1 = 0;
float subPathStartX = 0;
float subPathStartY = 0;
char prevCmd = 0;
while (ph.pos < n) {
char cmd = s.charAt(ph.pos);
switch (cmd) {
case '-':
case '+':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (prevCmd == 'm' || prevCmd == 'M') {
cmd = (char) (((int) prevCmd) - 1);
break;
} else if (prevCmd == 'c' || prevCmd == 'C') {
cmd = prevCmd;
break;
} else if (prevCmd == 'l' || prevCmd == 'L') {
cmd = prevCmd;
break;
}
default: {
ph.advance();
prevCmd = cmd;
}
}
boolean wasCurve = false;
switch (cmd) {
case 'M':
case 'm': {
float x = ph.nextFloat();
float y = ph.nextFloat();
if (cmd == 'm') {
subPathStartX += x;
subPathStartY += y;
p.rMoveTo(x, y);
lastX += x;
lastY += y;
} else {
subPathStartX = x;
subPathStartY = y;
p.moveTo(x, y);
lastX = x;
lastY = y;
}
break;
}
case 'Z':
case 'z': {
p.close();
p.moveTo(subPathStartX, subPathStartY);
lastX = subPathStartX;
lastY = subPathStartY;
lastX1 = subPathStartX;
lastY1 = subPathStartY;
wasCurve = true;
break;
}
case 'L':
case 'l': {
float x = ph.nextFloat();
float y = ph.nextFloat();
if (cmd == 'l') {
p.rLineTo(x, y);
lastX += x;
lastY += y;
} else {
p.lineTo(x, y);
lastX = x;
lastY = y;
}
break;
}
case 'H':
case 'h': {
float x = ph.nextFloat();
if (cmd == 'h') {
p.rLineTo(x, 0);
lastX += x;
} else {
p.lineTo(x, lastY);
lastX = x;
}
break;
}
case 'V':
case 'v': {
float y = ph.nextFloat();
if (cmd == 'v') {
p.rLineTo(0, y);
lastY += y;
} else {
p.lineTo(lastX, y);
lastY = y;
}
break;
}
case 'C':
case 'c': {
wasCurve = true;
float x1 = ph.nextFloat();
float y1 = ph.nextFloat();
float x2 = ph.nextFloat();
float y2 = ph.nextFloat();
float x = ph.nextFloat();
float y = ph.nextFloat();
if (cmd == 'c') {
x1 += lastX;
x2 += lastX;
x += lastX;
y1 += lastY;
y2 += lastY;
y += lastY;
}
p.cubicTo(x1, y1, x2, y2, x, y);
lastX1 = x2;
lastY1 = y2;
lastX = x;
lastY = y;
break;
}
case 'S':
case 's': {
wasCurve = true;
float x2 = ph.nextFloat();
float y2 = ph.nextFloat();
float x = ph.nextFloat();
float y = ph.nextFloat();
if (cmd == 's') {
x2 += lastX;
x += lastX;
y2 += lastY;
y += lastY;
}
float x1 = 2 * lastX - lastX1;
float y1 = 2 * lastY - lastY1;
p.cubicTo(x1, y1, x2, y2, x, y);
lastX1 = x2;
lastY1 = y2;
lastX = x;
lastY = y;
break;
}
case 'A':
case 'a': {
float rx = ph.nextFloat();
float ry = ph.nextFloat();
float theta = ph.nextFloat();
int largeArc = (int) ph.nextFloat();
int sweepArc = (int) ph.nextFloat();
float x = ph.nextFloat();
float y = ph.nextFloat();
drawArc(p, lastX, lastY, x, y, rx, ry, theta, largeArc, sweepArc);
lastX = x;
lastY = y;
break;
}
}
if (!wasCurve) {
lastX1 = lastX;
lastY1 = lastY;
}
ph.skipWhitespace();
}
return p;
}
private static void drawArc(Path p, float lastX, float lastY, float x, float y, float rx, float ry, float theta, int largeArc, int sweepArc) {
// todo - not implemented yet, may be very hard to do using Android drawing facilities.
}
private static NumberParse getNumberParseAttr(String name, Attributes attributes) {
int n = attributes.getLength();
for (int i = 0; i < n; i++) {
if (attributes.getLocalName(i).equals(name)) {
return parseNumbers(attributes.getValue(i));
}
}
return null;
}
private static String getStringAttr(String name, Attributes attributes) {
int n = attributes.getLength();
for (int i = 0; i < n; i++) {
if (attributes.getLocalName(i).equals(name)) {
return attributes.getValue(i);
}
}
return null;
}
private static Float getFloatAttr(String name, Attributes attributes) {
return getFloatAttr(name, attributes, null);
}
private static Float getFloatAttr(String name, Attributes attributes, Float defaultValue) {
String v = getStringAttr(name, attributes);
if (v == null) {
return defaultValue;
} else {
if (v.endsWith("px")) {
v = v.substring(0, v.length() - 2);
} else if (v.endsWith("mm")) {
return null;
}
return Float.parseFloat(v);
}
}
private static Integer getHexAttr(String name, Attributes attributes) {
String v = getStringAttr(name, attributes);
if (v == null) {
return null;
} else {
try {
return Integer.parseInt(v.substring(1), 16);
} catch (NumberFormatException nfe) {
return getColorByName(v);
}
}
}
private static Integer getColorByName(String name) {
switch (name.toLowerCase()) {
case "black":
return Color.BLACK;
case "gray":
return Color.GRAY;
case "red":
return Color.RED;
case "green":
return Color.GREEN;
case "blue":
return Color.BLUE;
case "yellow":
return Color.YELLOW;
case "cyan":
return Color.CYAN;
case "magenta":
return Color.MAGENTA;
case "white":
return Color.WHITE;
}
return null;
}
private static class NumberParse {
private ArrayList<Float> numbers;
private int nextCmd;
public NumberParse(ArrayList<Float> numbers, int nextCmd) {
this.numbers = numbers;
this.nextCmd = nextCmd;
}
public int getNextCmd() {
return nextCmd;
}
public float getNumber(int index) {
return numbers.get(index);
}
}
private static class StyleSet {
HashMap<String, String> styleMap = new HashMap<>();
private StyleSet(StyleSet styleSet) {
styleMap.putAll(styleSet.styleMap);
}
private StyleSet(String string) {
String[] styles = string.split(";");
for (String s : styles) {
String[] style = s.split(":");
if (style.length == 2) {
styleMap.put(style[0].trim(), style[1].trim());
}
}
}
public String getStyle(String name) {
return styleMap.get(name);
}
}
private static class Properties {
ArrayList<StyleSet> styles;
Attributes atts;
private Properties(Attributes atts, HashMap<String, StyleSet> globalStyles) {
this.atts = atts;
String styleAttr = getStringAttr("style", atts);
if (styleAttr != null) {
styles = new ArrayList<>();
styles.add(new StyleSet(styleAttr));
} else {
String classAttr = getStringAttr("class", atts);
if (classAttr != null) {
styles = new ArrayList<>();
String[] args = classAttr.split(" ");
for (int a = 0; a < args.length; a++) {
StyleSet set = globalStyles.get(args[a].trim());
if (set != null) {
styles.add(set);
}
}
}
}
}
public String getAttr(String name) {
String v = null;
if (styles != null && !styles.isEmpty()) {
for (int a = 0, N = styles.size(); a < N; a++) {
v = styles.get(a).getStyle(name);
if (v != null) {
break;
}
}
}
if (v == null) {
v = getStringAttr(name, atts);
}
return v;
}
public String getString(String name) {
return getAttr(name);
}
public Integer getHex(String name) {
String v = getAttr(name);
if (v == null) {
return null;
} else {
try {
return Integer.parseInt(v.substring(1), 16);
} catch (NumberFormatException nfe) {
return getColorByName(v);
}
}
}
public Float getFloat(String name, float defaultValue) {
Float v = getFloat(name);
if (v == null) {
return defaultValue;
} else {
return v;
}
}
public Float getFloat(String name) {
String v = getAttr(name);
if (v == null) {
return null;
} else {
try {
return Float.parseFloat(v);
} catch (NumberFormatException nfe) {
return null;
}
}
}
}
private static class SVGHandler extends DefaultHandler {
private Canvas canvas;
private Bitmap bitmap;
private int desiredWidth;
private int desiredHeight;
private float scale = 1.0f;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private RectF rect = new RectF();
private boolean whiteOnly;
boolean pushed = false;
private HashMap<String, StyleSet> globalStyles = new HashMap<>();
private SVGHandler(int dw, int dh, boolean white) {
desiredWidth = dw;
desiredHeight = dh;
whiteOnly = white;
}
@Override
public void startDocument() {
}
@Override
public void endDocument() {
}
private boolean doFill(Properties atts) {
if ("none".equals(atts.getString("display"))) {
return false;
}
String fillString = atts.getString("fill");
if (fillString != null && fillString.startsWith("url(#")) {
String id = fillString.substring("url(#".length(), fillString.length() - 1);
return false;
} else {
Integer color = atts.getHex("fill");
if (color != null) {
doColor(atts, color, true);
paint.setStyle(Paint.Style.FILL);
return true;
} else if (atts.getString("fill") == null && atts.getString("stroke") == null) {
paint.setStyle(Paint.Style.FILL);
if (whiteOnly) {
paint.setColor(0xffffffff);
} else {
paint.setColor(0xFF000000);
}
return true;
}
}
return false;
}
private boolean doStroke(Properties atts) {
if ("none".equals(atts.getString("display"))) {
return false;
}
Integer color = atts.getHex("stroke");
if (color != null) {
doColor(atts, color, false);
Float width = atts.getFloat("stroke-width");
if (width != null) {
paint.setStrokeWidth(width);
}
String linecap = atts.getString("stroke-linecap");
if ("round".equals(linecap)) {
paint.setStrokeCap(Paint.Cap.ROUND);
} else if ("square".equals(linecap)) {
paint.setStrokeCap(Paint.Cap.SQUARE);
} else if ("butt".equals(linecap)) {
paint.setStrokeCap(Paint.Cap.BUTT);
}
String linejoin = atts.getString("stroke-linejoin");
if ("miter".equals(linejoin)) {
paint.setStrokeJoin(Paint.Join.MITER);
} else if ("round".equals(linejoin)) {
paint.setStrokeJoin(Paint.Join.ROUND);
} else if ("bevel".equals(linejoin)) {
paint.setStrokeJoin(Paint.Join.BEVEL);
}
paint.setStyle(Paint.Style.STROKE);
return true;
}
return false;
}
private void doColor(Properties atts, Integer color, boolean fillMode) {
if (whiteOnly) {
paint.setColor(0xffffffff);
} else {
int c = (0xFFFFFF & color) | 0xFF000000;
paint.setColor(c);
}
Float opacity = atts.getFloat("opacity");
if (opacity == null) {
opacity = atts.getFloat(fillMode ? "fill-opacity" : "stroke-opacity");
}
if (opacity == null) {
paint.setAlpha(255);
} else {
paint.setAlpha((int) (255 * opacity));
}
}
private boolean boundsMode;
private StringBuilder styles;
private void pushTransform(Attributes atts) {
final String transform = getStringAttr("transform", atts);
pushed = transform != null;
if (pushed) {
final Matrix matrix = parseTransform(transform);
canvas.save();
canvas.concat(matrix);
}
}
private void popTransform() {
if (pushed) {
canvas.restore();
}
}
@Override
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) {
if (boundsMode && !localName.equals("style")) {
return;
}
switch (localName) {
case "svg": {
Float w = getFloatAttr("width", atts);
Float h = getFloatAttr("height", atts);
if (w == null || h == null) {
String viewBox = getStringAttr("viewBox", atts);
if (viewBox != null) {
String[] args = viewBox.split(" ");
w = Float.parseFloat(args[2]);
h = Float.parseFloat(args[3]);
}
}
if (w == null || h == null) {
w = (float) desiredWidth;
h = (float) desiredHeight;
}
int width = (int) Math.ceil(w);
int height = (int) Math.ceil(h);
if (width == 0 || height == 0) {
width = desiredWidth;
height = desiredHeight;
} else {
scale = Math.min(desiredWidth / (float) width, desiredHeight / (float) height);
width *= scale;
height *= scale;
}
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.eraseColor(0);
canvas = new Canvas(bitmap);
if (scale != 0) {
canvas.scale(scale, scale);
}
break;
}
case "defs":
case "clipPath":
boundsMode = true;
break;
case "style":
styles = new StringBuilder();
break;
case "g":
if ("bounds".equalsIgnoreCase(getStringAttr("id", atts))) {
boundsMode = true;
}
break;
case "rect": {
Float x = getFloatAttr("x", atts);
if (x == null) {
x = 0f;
}
Float y = getFloatAttr("y", atts);
if (y == null) {
y = 0f;
}
Float width = getFloatAttr("width", atts);
Float height = getFloatAttr("height", atts);
pushTransform(atts);
Properties props = new Properties(atts, globalStyles);
if (doFill(props)) {
canvas.drawRect(x, y, x + width, y + height, paint);
}
if (doStroke(props)) {
canvas.drawRect(x, y, x + width, y + height, paint);
}
popTransform();
break;
}
case "line": {
Float x1 = getFloatAttr("x1", atts);
Float x2 = getFloatAttr("x2", atts);
Float y1 = getFloatAttr("y1", atts);
Float y2 = getFloatAttr("y2", atts);
Properties props = new Properties(atts, globalStyles);
if (doStroke(props)) {
pushTransform(atts);
canvas.drawLine(x1, y1, x2, y2, paint);
popTransform();
}
break;
}
case "circle": {
Float centerX = getFloatAttr("cx", atts);
Float centerY = getFloatAttr("cy", atts);
Float radius = getFloatAttr("r", atts);
if (centerX != null && centerY != null && radius != null) {
pushTransform(atts);
Properties props = new Properties(atts, globalStyles);
if (doFill(props)) {
canvas.drawCircle(centerX, centerY, radius, paint);
}
if (doStroke(props)) {
canvas.drawCircle(centerX, centerY, radius, paint);
}
popTransform();
}
break;
}
case "ellipse": {
Float centerX = getFloatAttr("cx", atts);
Float centerY = getFloatAttr("cy", atts);
Float radiusX = getFloatAttr("rx", atts);
Float radiusY = getFloatAttr("ry", atts);
if (centerX != null && centerY != null && radiusX != null && radiusY != null) {
pushTransform(atts);
Properties props = new Properties(atts, globalStyles);
rect.set(centerX - radiusX, centerY - radiusY, centerX + radiusX, centerY + radiusY);
if (doFill(props)) {
canvas.drawOval(rect, paint);
}
if (doStroke(props)) {
canvas.drawOval(rect, paint);
}
popTransform();
}
break;
}
case "polygon":
case "polyline":
NumberParse numbers = getNumberParseAttr("points", atts);
if (numbers != null) {
Path p = new Path();
ArrayList<Float> points = numbers.numbers;
if (points.size() > 1) {
pushTransform(atts);
Properties props = new Properties(atts, globalStyles);
p.moveTo(points.get(0), points.get(1));
for (int i = 2; i < points.size(); i += 2) {
float x = points.get(i);
float y = points.get(i + 1);
p.lineTo(x, y);
}
if (localName.equals("polygon")) {
p.close();
}
if (doFill(props)) {
canvas.drawPath(p, paint);
}
if (doStroke(props)) {
canvas.drawPath(p, paint);
}
popTransform();
}
}
break;
case "path": {
Path p = doPath(getStringAttr("d", atts));
pushTransform(atts);
Properties props = new Properties(atts, globalStyles);
if (doFill(props)) {
canvas.drawPath(p, paint);
}
if (doStroke(props)) {
canvas.drawPath(p, paint);
}
popTransform();
break;
}
}
}
@Override
public void characters(char[] ch, int start, int length) {
if (styles != null) {
styles.append(ch, start, length);
}
}
@Override
public void endElement(String namespaceURI, String localName, String qName) {
switch (localName) {
case "style":
if (styles != null) {
String[] args = styles.toString().split("\\}");
for (int a = 0; a < args.length; a++) {
args[a] = args[a].trim().replace("\t", "").replace("\n", "");
if (args[a].length() == 0 || args[a].charAt(0) != '.') {
continue;
}
int idx1 = args[a].indexOf('{');
if (idx1 < 0) {
continue;
}
String name = args[a].substring(1, idx1).trim();
String style = args[a].substring(idx1 + 1);
globalStyles.put(name, new StyleSet(style));
}
styles = null;
}
break;
case "svg":
break;
case "g":
case "defs":
case "clipPath":
boundsMode = false;
break;
}
}
public Bitmap getBitmap() {
return bitmap;
}
}
private static final double[] pow10 = new double[128];
static {
for (int i = 0; i < pow10.length; i++) {
pow10[i] = Math.pow(10, i);
}
}
public static class ParserHelper {
private char current;
private CharSequence s;
public int pos;
private int n;
public ParserHelper(CharSequence s, int pos) {
this.s = s;
this.pos = pos;
n = s.length();
current = s.charAt(pos);
}
private char read() {
if (pos < n) {
pos++;
}
if (pos == n) {
return '\0';
} else {
return s.charAt(pos);
}
}
public void skipWhitespace() {
while (pos < n) {
if (Character.isWhitespace(s.charAt(pos))) {
advance();
} else {
break;
}
}
}
public void skipNumberSeparator() {
while (pos < n) {
char c = s.charAt(pos);
switch (c) {
case ' ':
case ',':
case '\n':
case '\t':
advance();
break;
default:
return;
}
}
}
public void advance() {
current = read();
}
public float parseFloat() {
int mant = 0;
int mantDig = 0;
boolean mantPos = true;
boolean mantRead = false;
int exp = 0;
int expDig = 0;
int expAdj = 0;
boolean expPos = true;
switch (current) {
case '-':
mantPos = false;
// fallthrough
case '+':
current = read();
}
m1:
switch (current) {
default:
return Float.NaN;
case '.':
break;
case '0':
mantRead = true;
l:
for (; ; ) {
current = read();
switch (current) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break l;
case '.':
case 'e':
case 'E':
break m1;
default:
return 0.0f;
case '0':
}
}
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
mantRead = true;
l:
for (; ; ) {
if (mantDig < 9) {
mantDig++;
mant = mant * 10 + (current - '0');
} else {
expAdj++;
}
current = read();
switch (current) {
default:
break l;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
}
}
}
if (current == '.') {
current = read();
m2:
switch (current) {
default:
case 'e':
case 'E':
if (!mantRead) {
reportUnexpectedCharacterError(current);
return 0.0f;
}
break;
case '0':
if (mantDig == 0) {
l:
for (; ; ) {
current = read();
expAdj--;
switch (current) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break l;
default:
if (!mantRead) {
return 0.0f;
}
break m2;
case '0':
}
}
}
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
l:
for (; ; ) {
if (mantDig < 9) {
mantDig++;
mant = mant * 10 + (current - '0');
expAdj--;
}
current = read();
switch (current) {
default:
break l;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
}
}
}
}
switch (current) {
case 'e':
case 'E':
current = read();
switch (current) {
default:
reportUnexpectedCharacterError(current);
return 0f;
case '-':
expPos = false;
case '+':
current = read();
switch (current) {
default:
reportUnexpectedCharacterError(current);
return 0f;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
}
en:
switch (current) {
case '0':
l:
for (; ; ) {
current = read();
switch (current) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break l;
default:
break en;
case '0':
}
}
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
l:
for (; ; ) {
if (expDig < 3) {
expDig++;
exp = exp * 10 + (current - '0');
}
current = read();
switch (current) {
default:
break l;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
}
}
}
default:
}
if (!expPos) {
exp = -exp;
}
exp += expAdj;
if (!mantPos) {
mant = -mant;
}
return buildFloat(mant, exp);
}
private void reportUnexpectedCharacterError(char c) {
throw new RuntimeException("Unexpected char '" + c + "'.");
}
public float buildFloat(int mant, int exp) {
if (exp < -125 || mant == 0) {
return 0.0f;
}
if (exp >= 128) {
return (mant > 0) ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY;
}
if (exp == 0) {
return mant;
}
if (mant >= (1 << 26)) {
mant++;
}
return (float) ((exp > 0) ? mant * pow10[exp] : mant / pow10[-exp]);
}
public float nextFloat() {
skipWhitespace();
float f = parseFloat();
skipNumberSeparator();
return f;
}
}
}