org.apache.commons.imaging.common.BasicCParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of commons-imaging Show documentation
Show all versions of commons-imaging Show documentation
Apache Commons Imaging (previously Sanselan) is a pure-Java image library.
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.commons.imaging.common;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.Map;
import org.apache.commons.imaging.ImageReadException;
/**
* A rudimentary preprocessor and parser for the C programming
* language.
*
* FIXME replace this by a parser generated via ANTLR (if we really need it?!)
*/
public class BasicCParser {
private final PushbackInputStream is;
public BasicCParser(final ByteArrayInputStream is) {
this.is = new PushbackInputStream(is);
}
public String nextToken() throws IOException, ImageReadException {
// I don't know how complete the C parsing in an XPM file
// is meant to be, this is just the very basics...
boolean inString = false;
boolean inIdentifier = false;
boolean hadBackSlash = false;
final StringBuilder token = new StringBuilder();
for (int c = is.read(); c != -1; c = is.read()) {
if (inString) {
if (c == '\\') {
token.append('\\');
hadBackSlash = !hadBackSlash;
} else if (c == '"') {
token.append('"');
if (!hadBackSlash) {
return token.toString();
}
hadBackSlash = false;
} else if (c == '\r' || c == '\n') {
throw new ImageReadException(
"Unterminated string in XPM file");
} else {
token.append((char) c);
hadBackSlash = false;
}
} else if (inIdentifier) {
if (Character.isLetterOrDigit(c) || c == '_') {
token.append((char) c);
} else {
is.unread(c);
return token.toString();
}
} else {
if (c == '"') {
token.append('"');
inString = true;
} else if (Character.isLetterOrDigit(c) || c == '_') {
token.append((char) c);
inIdentifier = true;
} else if (c == '{' || c == '}' || c == '[' || c == ']'
|| c == '*' || c == ';' || c == '=' || c == ',') {
token.append((char) c);
return token.toString();
} else if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
// ignore
} else {
throw new ImageReadException(
"Unhandled/invalid character '" + ((char) c)
+ "' found in XPM file");
}
}
}
if (inIdentifier) {
return token.toString();
}
if (inString) {
throw new ImageReadException("Unterminated string ends XMP file");
}
return null;
}
public static ByteArrayOutputStream preprocess(final InputStream is,
final StringBuilder firstComment, final Map defines)
throws IOException, ImageReadException {
boolean inSingleQuotes = false;
boolean inString = false;
boolean inComment = false;
boolean inDirective = false;
boolean hadSlash = false;
boolean hadStar = false;
boolean hadBackSlash = false;
final ByteArrayOutputStream out = new ByteArrayOutputStream();
boolean seenFirstComment = (firstComment == null);
final StringBuilder directiveBuffer = new StringBuilder();
for (int c = is.read(); c != -1; c = is.read()) {
if (inComment) {
if (c == '*') {
if (hadStar && !seenFirstComment) {
firstComment.append('*');
}
hadStar = true;
} else if (c == '/') {
if (hadStar) {
hadStar = false;
inComment = false;
seenFirstComment = true;
} else {
if (!seenFirstComment) {
firstComment.append((char) c);
}
}
} else {
if (hadStar && !seenFirstComment) {
firstComment.append('*');
}
hadStar = false;
if (!seenFirstComment) {
firstComment.append((char) c);
}
}
} else if (inSingleQuotes) {
if (c == '\\') {
if (hadBackSlash) {
out.write('\\');
out.write('\\');
hadBackSlash = false;
} else {
hadBackSlash = true;
}
} else if (c == '\'') {
if (hadBackSlash) {
out.write('\\');
hadBackSlash = false;
} else {
inSingleQuotes = false;
}
out.write('\'');
} else if (c == '\r' || c == '\n') {
throw new ImageReadException("Unterminated single quote in file");
} else {
if (hadBackSlash) {
out.write('\\');
hadBackSlash = false;
}
out.write(c);
}
} else if (inString) {
if (c == '\\') {
if (hadBackSlash) {
out.write('\\');
out.write('\\');
hadBackSlash = false;
} else {
hadBackSlash = true;
}
} else if (c == '"') {
if (hadBackSlash) {
out.write('\\');
hadBackSlash = false;
} else {
inString = false;
}
out.write('"');
} else if (c == '\r' || c == '\n') {
throw new ImageReadException("Unterminated string in file");
} else {
if (hadBackSlash) {
out.write('\\');
hadBackSlash = false;
}
out.write(c);
}
} else if (inDirective) {
if (c == '\r' || c == '\n') {
inDirective = false;
final String[] tokens = tokenizeRow(directiveBuffer.toString());
if (tokens.length < 2 || tokens.length > 3) {
throw new ImageReadException("Bad preprocessor directive");
}
if (!tokens[0].equals("define")) {
throw new ImageReadException("Invalid/unsupported "
+ "preprocessor directive '" + tokens[0] + "'");
}
defines.put(tokens[1], (tokens.length == 3) ? tokens[2]
: null);
directiveBuffer.setLength(0);
} else {
directiveBuffer.append((char) c);
}
} else {
if (c == '/') {
if (hadSlash) {
out.write('/');
}
hadSlash = true;
} else if (c == '*') {
if (hadSlash) {
inComment = true;
hadSlash = false;
} else {
out.write(c);
}
} else if (c == '\'') {
if (hadSlash) {
out.write('/');
}
hadSlash = false;
out.write(c);
inSingleQuotes = true;
} else if (c == '"') {
if (hadSlash) {
out.write('/');
}
hadSlash = false;
out.write(c);
inString = true;
} else if (c == '#') {
if (defines == null) {
throw new ImageReadException("Unexpected preprocessor directive");
}
inDirective = true;
} else {
if (hadSlash) {
out.write('/');
}
hadSlash = false;
out.write(c);
// Only whitespace allowed before first comment:
if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
seenFirstComment = true;
}
}
}
}
if (hadSlash) {
out.write('/');
}
if (hadStar) {
out.write('*');
}
if (inString) {
throw new ImageReadException("Unterminated string at the end of file");
}
if (inComment) {
throw new ImageReadException("Unterminated comment at the end of file");
}
return out;
}
public static String[] tokenizeRow(final String row) {
final String[] tokens = row.split("[ \t]");
int numLiveTokens = 0;
for (final String token : tokens) {
if (token != null && token.length() > 0) {
++numLiveTokens;
}
}
final String[] liveTokens = new String[numLiveTokens];
int next = 0;
for (final String token : tokens) {
if (token != null && token.length() > 0) {
liveTokens[next++] = token;
}
}
return liveTokens;
}
public static void unescapeString(final StringBuilder stringBuilder, final String string)
throws ImageReadException {
if (string.length() < 2) {
throw new ImageReadException("Parsing XPM file failed, "
+ "string is too short");
}
if (string.charAt(0) != '"'
|| string.charAt(string.length() - 1) != '"') {
throw new ImageReadException("Parsing XPM file failed, "
+ "string not surrounded by '\"'");
}
boolean hadBackSlash = false;
for (int i = 1; i < (string.length() - 1); i++) {
final char c = string.charAt(i);
if (hadBackSlash) {
if (c == '\\') {
stringBuilder.append('\\');
} else if (c == '"') {
stringBuilder.append('"');
} else if (c == '\'') {
stringBuilder.append('\'');
} else if (c == 'x') {
if (i + 2 >= string.length()) {
throw new ImageReadException(
"Parsing XPM file failed, "
+ "hex constant in string too short");
}
final char hex1 = string.charAt(i + 1);
final char hex2 = string.charAt(i + 2);
i += 2;
int constant;
try {
constant = Integer.parseInt(Character.toString(hex1) + Character.toString(hex2), 16);
} catch (final NumberFormatException nfe) {
throw new ImageReadException(
"Parsing XPM file failed, "
+ "hex constant invalid", nfe);
}
stringBuilder.append((char) constant);
} else if (c == '0' || c == '1' || c == '2' || c == '3'
|| c == '4' || c == '5' || c == '6' || c == '7') {
int length = 1;
if (i + 1 < string.length() && '0' <= string.charAt(i + 1)
&& string.charAt(i + 1) <= '7') {
++length;
}
if (i + 2 < string.length() && '0' <= string.charAt(i + 2)
&& string.charAt(i + 2) <= '7') {
++length;
}
int constant = 0;
for (int j = 0; j < length; j++) {
constant *= 8;
constant += (string.charAt(i + j) - '0');
}
i += length - 1;
stringBuilder.append((char) constant);
} else if (c == 'a') {
stringBuilder.append((char) 0x07);
} else if (c == 'b') {
stringBuilder.append((char) 0x08);
} else if (c == 'f') {
stringBuilder.append((char) 0x0c);
} else if (c == 'n') {
stringBuilder.append((char) 0x0a);
} else if (c == 'r') {
stringBuilder.append((char) 0x0d);
} else if (c == 't') {
stringBuilder.append((char) 0x09);
} else if (c == 'v') {
stringBuilder.append((char) 0x0b);
} else {
throw new ImageReadException("Parsing XPM file failed, "
+ "invalid escape sequence");
}
hadBackSlash = false;
} else {
if (c == '\\') {
hadBackSlash = true;
} else if (c == '"') {
throw new ImageReadException("Parsing XPM file failed, "
+ "extra '\"' found in string");
} else {
stringBuilder.append(c);
}
}
}
if (hadBackSlash) {
throw new ImageReadException("Parsing XPM file failed, "
+ "unterminated escape sequence found in string");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy