/*
Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
и других спецификаций для функционирования компактных приложений на языке
Java (мидлетов) в среде программного обеспечения Малик Эмулятор.
Copyright © 2016, 2019 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package malik.emulator.fileformats.text.mapped;
import java.io.*;
import malik.emulator.fileformats.*;
import malik.emulator.i18n.*;
public class PropertiesFile extends CustomKeyValueList
{
public static final char ESCAPE = '\\';
public static final char COMMENT_START_1 = '!';
public static final char COMMENT_START_2 = '#';
public static final char KEY_VALUE_SEPARATOR_1 = ' ';
public static final char KEY_VALUE_SEPARATOR_2 = ':';
public static final char KEY_VALUE_SEPARATOR_3 = '=';
public static final char UNICODE_CHAR = 'u';
public static final String ENCODING = "ISO-8859-1";
/* Условие выхода из этого метода:
* c == 0 || c == 10 || c == 13 || c > 32
* где c = s[i], i - возвращаемое значение метода. */
private static int skipWhiteSpace(char[] stream, int offset)
{
int c;
int i = offset;
for(; (c = stream[i]) > 0 && c <= 32 && c != 10 && c != 13; i++);
return i;
}
/* Условие выхода из этого метода:
* c == 0 || c > 32
* где c = s[i], i - возвращаемое значение метода. */
private static int skipEmptySpace(char[] stream, int offset)
{
int c;
int i = offset;
for(; (c = stream[i]) > 0 && c <= 32; i++);
return i;
}
/* Этот метод читает до конца строки
* и останавливается после символа перевода строки. */
private static int skipComment(char[] stream, int offset)
{
int c;
int i = offset;
for(i++; (c = stream[i]) != 0 && c != 10 && c != 13; i++);
if(c != 0)
{
i += c == 13 && stream[i + 1] == 10 ? 2 : 1;
}
return i;
}
private String key;
private String value;
public PropertiesFile()
{
}
public void loadFromInputStream(InputStream stream)
throws IOException, InvalidDataFormatException
{
loadFromInputStream(stream, ENCODING);
}
public void loadFromInputStream(InputStream stream, String encoding)
throws IOException, InvalidDataFormatException, UnsupportedEncodingException
{
int c;
int i;
int size;
char[] s;
byte[] b;
stream.read(b = new byte[(size = stream.available()) + 4], 0, size);
s = Helper.byteToCharArray(b, 0, size + 4, encoding);
clear();
try
{
i = size >= 1 && s[0] == 0xfeff ? 1 : 0;
for(; s[i = skipEmptySpace(s, i)] != 0 && i < size; set(key, value))
{
while((c = s[i]) == COMMENT_START_1 || c == COMMENT_START_2)
{
i = skipEmptySpace(s, skipComment(s, i));
}
if(s[i] == 0 || i >= size)
{
break;
}
if((c = s[i = skipWhiteSpace(s, parseKey(s, i))]) == KEY_VALUE_SEPARATOR_2 ||
c == KEY_VALUE_SEPARATOR_3)
{
i = skipWhiteSpace(s, i + 1);
}
i = parseValue(s, i);
}
}
finally
{
key = null;
value = null;
}
}
public void loadFromDataStream(DataInputStream stream)
throws IOException, InvalidDataFormatException
{
loadFromInputStream(stream, ENCODING);
}
public void loadFromDataStream(DataInputStream stream, String encoding)
throws IOException, InvalidDataFormatException, UnsupportedEncodingException
{
loadFromInputStream(stream, encoding);
}
private int parseKey(char[] stream, int offset)
{
int i = offset;
int j;
int c;
int u;
StringBuffer result = new StringBuffer();
do
{
for(; (c = stream[i]) > 32 && c != ESCAPE &&
c != KEY_VALUE_SEPARATOR_2 && c != KEY_VALUE_SEPARATOR_3; i++);
if(i > offset)
{
result.append(stream, offset, i - offset);
}
if(c != ESCAPE)
{
break;
}
switch(c = stream[++i])
{
default:
break;
case 13:
if(stream[i + 1] == 10)
{
i++;
}
/* fall through */
case 10:
i = skipWhiteSpace(stream, i + 1);
break;
case UNICODE_CHAR:
i++;
u = 0;
for(j = 4; j-- > 0; i++)
{
if((c = stream[i]) >= '0' && c <= '9')
{
u = (u << 4) + (c - '0');
}
else if(c >= 'A' && c <= 'F')
{
u = (u << 4) + (c - ('A' - 0x0a));
}
else if(c >= 'a' && c <= 'f')
{
u = (u << 4) + (c - ('a' - 0x0a));
}
else
{
break;
}
}
result.append((char) u);
break;
case ESCAPE:
case COMMENT_START_1:
case COMMENT_START_2:
case KEY_VALUE_SEPARATOR_1:
case KEY_VALUE_SEPARATOR_2:
case KEY_VALUE_SEPARATOR_3:
i++;
result.append((char) c);
break;
}
offset = i;
} while(true);
key = result.toString().intern();
return i;
}
private int parseValue(char[] stream, int offset)
{
int i = offset;
int j;
int c;
int u;
StringBuffer result = new StringBuffer();
do
{
for(; (c = stream[i]) >= 32 && c != ESCAPE; i++);
if(i > offset)
{
result.append(stream, offset, i - offset);
}
if(c != ESCAPE)
{
break;
}
switch(c = stream[++i])
{
default:
break;
case 13:
if(stream[i + 1] == 10)
{
i++;
}
/* fall through */
case 10:
i = skipWhiteSpace(stream, i + 1);
break;
case UNICODE_CHAR:
i++;
u = 0;
for(j = 4; j-- > 0; i++)
{
if((c = stream[i]) >= '0' && c <= '9')
{
u = (u << 4) + (c - '0');
}
else if(c >= 'A' && c <= 'F')
{
u = (u << 4) + (c - ('A' - 0x0a));
}
else if(c >= 'a' && c <= 'f')
{
u = (u << 4) + (c - ('a' - 0x0a));
}
else
{
break;
}
}
result.append((char) u);
break;
case ESCAPE:
case COMMENT_START_1:
case COMMENT_START_2:
case KEY_VALUE_SEPARATOR_1:
case KEY_VALUE_SEPARATOR_2:
case KEY_VALUE_SEPARATOR_3:
i++;
result.append((char) c);
break;
}
offset = i;
} while(true);
value = result.toString().intern();
return i;
}
}