com.adobe.internal.xmp.impl.FixASCIIControlsReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xmpcore Show documentation
Show all versions of xmpcore Show documentation
The XMP Library for Java is based on the C++ XMPCore library
and the API is similar.
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
// of the Adobe license agreement accompanying it.
// =================================================================================================
package com.adobe.internal.xmp.impl;
import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
/**
* @author Stefan Makswit
* @version $Revision$
* @since 22.08.2006
*/
public class FixASCIIControlsReader extends PushbackReader
{
/** */
private static final int STATE_START = 0;
/** */
private static final int STATE_AMP = 1;
/** */
private static final int STATE_HASH = 2;
/** */
private static final int STATE_HEX = 3;
/** */
private static final int STATE_DIG1 = 4;
/** */
private static final int STATE_ERROR = 5;
/** */
private static final int BUFFER_SIZE = 8;
/** the state of the automaton */
private int state = STATE_START;
/** the result of the escaping sequence */
private int control = 0;
/** count the digits of the sequence */
private int digits = 0;
/**
* The look-ahead size is 6 at maximum («)
* @see PushbackReader#PushbackReader(Reader, int)
* @param in a Reader
*/
public FixASCIIControlsReader(Reader in)
{
super(in, BUFFER_SIZE);
}
/**
* @see Reader#read(char[], int, int)
*/
public int read(char[] cbuf, int off, int len) throws IOException
{
int readAhead = 0;
int read = 0;
int pos = off;
char[] readAheadBuffer = new char[BUFFER_SIZE];
boolean available = true;
while (available && read < len)
{
available = super.read(readAheadBuffer, readAhead, 1) == 1;
if (available)
{
char c = processChar(readAheadBuffer[readAhead]);
if (state == STATE_START)
{
// replace control chars with space
if (Utils.isControlChar(c))
{
c = ' ';
}
cbuf[pos++] = c;
readAhead = 0;
read++;
}
else if (state == STATE_ERROR)
{
unread(readAheadBuffer, 0, readAhead + 1);
readAhead = 0;
}
else
{
readAhead++;
}
}
else if (readAhead > 0)
{
// handles case when file ends within excaped sequence
unread(readAheadBuffer, 0, readAhead);
state = STATE_ERROR;
readAhead = 0;
available = true;
}
}
return read > 0 || available ? read : -1;
}
/**
* Processes numeric escaped chars to find out if they are a control character.
* @param ch a char
* @return Returns the char directly or as replacement for the escaped sequence.
*/
private char processChar(char ch)
{
switch (state)
{
case STATE_START:
if (ch == '&')
{
state = STATE_AMP;
}
return ch;
case STATE_AMP:
if (ch == '#')
{
state = STATE_HASH;
}
else
{
state = STATE_ERROR;
}
return ch;
case STATE_HASH:
if (ch == 'x')
{
control = 0;
digits = 0;
state = STATE_HEX;
}
else if ('0' <= ch && ch <= '9')
{
control = Character.digit(ch, 10);
digits = 1;
state = STATE_DIG1;
}
else
{
state = STATE_ERROR;
}
return ch;
case STATE_DIG1:
if ('0' <= ch && ch <= '9')
{
control = control * 10 + Character.digit(ch, 10);
digits++;
if (digits <= 5)
{
state = STATE_DIG1;
}
else
{
state = STATE_ERROR; // sequence too long
}
}
else if (ch == ';' && Utils.isControlChar((char) control))
{
state = STATE_START;
return (char) control;
}
else
{
state = STATE_ERROR;
}
return ch;
case STATE_HEX:
if (('0' <= ch && ch <= '9') ||
('a' <= ch && ch <= 'f') ||
('A' <= ch && ch <= 'F'))
{
control = control * 16 + Character.digit(ch, 16);
digits++;
if (digits <= 4)
{
state = STATE_HEX;
}
else
{
state = STATE_ERROR; // sequence too long
}
}
else if (ch == ';' && Utils.isControlChar((char) control))
{
state = STATE_START;
return (char) control;
}
else
{
state = STATE_ERROR;
}
return ch;
case STATE_ERROR:
state = STATE_START;
return ch;
default:
// not reachable
return ch;
}
}
}