}.
*
* @see #getPathStrategy(Context, String)
*/
private static PathStrategy parsePathStrategy(Context context, String authority)
throws IOException, XmlPullParserException {
final SimplePathStrategy strat = new SimplePathStrategy(authority);
final ProviderInfo info = context.getPackageManager()
.resolveContentProvider(authority, PackageManager.GET_META_DATA);
final XmlResourceParser in = info.loadXmlMetaData(
context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS);
if (in == null) {
throw new IllegalArgumentException(
"Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data");
}
int type;
while ((type = in.next()) != END_DOCUMENT) {
if (type == START_TAG) {
final String tag = in.getName();
final String name = in.getAttributeValue(null, ATTR_NAME);
String path = in.getAttributeValue(null, ATTR_PATH);
File target = null;
if (TAG_ROOT_PATH.equals(tag)) {
target = DEVICE_ROOT;
} else if (TAG_FILES_PATH.equals(tag)) {
target = context.getFilesDir();
} else if (TAG_CACHE_PATH.equals(tag)) {
target = context.getCacheDir();
} else if (TAG_EXTERNAL.equals(tag)) {
target = Environment.getExternalStorageDirectory();
} else if (TAG_EXTERNAL_FILES.equals(tag)) {
target = context.getExternalFilesDir(null);
} else if (TAG_EXTERNAL_CACHE.equals(tag)) {
target = context.getExternalCacheDir();
}
if (target != null) {
strat.addRoot(name, buildPath(target, path));
}
}
}
return strat;
}
/**
* Strategy for mapping between {@link File} and {@link Uri}.
*
* Strategies must be symmetric so that mapping a {@link File} to a
* {@link Uri} and then back to a {@link File} points at the original
* target.
*
* Strategies must remain consistent across app launches, and not rely on
* dynamic state. This ensures that any generated {@link Uri} can still be
* resolved if your process is killed and later restarted.
*
* @see SimplePathStrategy
*/
interface PathStrategy {
/**
* Return a {@link Uri} that represents the given {@link File}.
*/
public Uri getUriForFile(File file);
/**
* Return a {@link File} that represents the given {@link Uri}.
*/
public File getFileForUri(Uri uri);
}
/**
* Strategy that provides access to files living under a narrow whitelist of
* filesystem roots. It will throw {@link SecurityException} if callers try
* accessing files outside the configured roots.
*
* For example, if configured with
* {@code addRoot("myfiles", context.getFilesDir())}, then
* {@code context.getFileStreamPath("foo.txt")} would map to
* {@code content://myauthority/myfiles/foo.txt}.
*/
static class SimplePathStrategy implements PathStrategy {
private final String mAuthority;
private final HashMap mRoots = new HashMap();
public SimplePathStrategy(String authority) {
mAuthority = authority;
}
/**
* Add a mapping from a name to a filesystem root. The provider only offers
* access to files that live under configured roots.
*/
public void addRoot(String name, File root) {
if (TextUtils.isEmpty(name)) {
throw new IllegalArgumentException("Name must not be empty");
}
try {
// Resolve to canonical path to keep path checking fast
root = root.getCanonicalFile();
} catch (IOException e) {
throw new IllegalArgumentException(
"Failed to resolve canonical path for " + root, e);
}
mRoots.put(name, root);
}
@Override
public Uri getUriForFile(File file) {
String path;
try {
path = file.getCanonicalPath();
} catch (IOException e) {
throw new IllegalArgumentException("Failed to resolve canonical path for " + file);
}
// Find the most-specific root path
Map.Entry mostSpecific = null;
for (Map.Entry root : mRoots.entrySet()) {
final String rootPath = root.getValue().getPath();
if (path.startsWith(rootPath) && (mostSpecific == null
|| rootPath.length() > mostSpecific.getValue().getPath().length())) {
mostSpecific = root;
}
}
if (mostSpecific == null) {
throw new IllegalArgumentException(
"Failed to find configured root that contains " + path);
}
// Start at first char of path under root
final String rootPath = mostSpecific.getValue().getPath();
if (rootPath.endsWith("/")) {
path = path.substring(rootPath.length());
} else {
path = path.substring(rootPath.length() + 1);
}
// Encode the tag and path separately
path = Uri.encode(mostSpecific.getKey()) + '/' + Uri.encode(path, "/");
return new Uri.Builder().scheme("content")
.authority(mAuthority).encodedPath(path).build();
}
@Override
public File getFileForUri(Uri uri) {
String path = uri.getEncodedPath();
final int splitIndex = path.indexOf('/', 1);
final String tag = Uri.decode(path.substring(1, splitIndex));
path = Uri.decode(path.substring(splitIndex + 1));
final File root = mRoots.get(tag);
if (root == null) {
throw new IllegalArgumentException("Unable to find configured root for " + uri);
}
File file = new File(root, path);
try {
file = file.getCanonicalFile();
} catch (IOException e) {
throw new IllegalArgumentException("Failed to resolve canonical path for " + file);
}
if (!file.getPath().startsWith(root.getPath())) {
throw new SecurityException("Resolved path jumped beyond configured root");
}
return file;
}
}
/**
* Copied from ContentResolver.java
*/
private static int modeToMode(String mode) {
int modeBits;
if ("r".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
} else if ("w".equals(mode) || "wt".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
| ParcelFileDescriptor.MODE_CREATE
| ParcelFileDescriptor.MODE_TRUNCATE;
} else if ("wa".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
| ParcelFileDescriptor.MODE_CREATE
| ParcelFileDescriptor.MODE_APPEND;
} else if ("rw".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_READ_WRITE
| ParcelFileDescriptor.MODE_CREATE;
} else if ("rwt".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_READ_WRITE
| ParcelFileDescriptor.MODE_CREATE
| ParcelFileDescriptor.MODE_TRUNCATE;
} else {
throw new IllegalArgumentException("Invalid mode: " + mode);
}
return modeBits;
}
private static File buildPath(File base, String... segments) {
File cur = base;
for (String segment : segments) {
if (segment != null) {
cur = new File(cur, segment);
}
}
return cur;
}
private static String[] copyOf(String[] original, int newLength) {
final String[] result = new String[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
private static Object[] copyOf(Object[] original, int newLength) {
final Object[] result = new Object[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
}
================================================
FILE: src/main/java/com/ibm/icu/text/CharsetDetector.java
================================================
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/**
*******************************************************************************
* Copyright (C) 2005-2016, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.text;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* CharsetDetector provides a facility for detecting the
* charset or encoding of character data in an unknown format.
* The input data can either be from an input stream or an array of bytes.
* The result of the detection operation is a list of possibly matching
* charsets, or, for simple use, you can just ask for a Java Reader that
* will will work over the input data.
*
* Character set detection is at best an imprecise operation. The detection
* process will attempt to identify the charset that best matches the characteristics
* of the byte data, but the process is partly statistical in nature, and
* the results can not be guaranteed to always be correct.
*
* For best accuracy in charset detection, the input data should be primarily
* in a single language, and a minimum of a few hundred bytes worth of plain text
* in the language are needed. The detection process will attempt to
* ignore html or xml style markup that could otherwise obscure the content.
*
* @stable ICU 3.4
*/
public class CharsetDetector {
// Question: Should we have getters corresponding to the setters for input text
// and declared encoding?
// A thought: If we were to create our own type of Java Reader, we could defer
// figuring out an actual charset for data that starts out with too much English
// only ASCII until the user actually read through to something that didn't look
// like 7 bit English. If nothing else ever appeared, we would never need to
// actually choose the "real" charset. All assuming that the application just
// wants the data, and doesn't care about a char set name.
/**
* Constructor
*
* @stable ICU 3.4
*/
public CharsetDetector() {
}
/**
* Set the declared encoding for charset detection.
* The declared encoding of an input text is an encoding obtained
* from an http header or xml declaration or similar source that
* can be provided as additional information to the charset detector.
* A match between a declared encoding and a possible detected encoding
* will raise the quality of that detected encoding by a small delta,
* and will also appear as a "reason" for the match.
*
* A declared encoding that is incompatible with the input data being
* analyzed will not be added to the list of possible encodings.
*
* @param encoding The declared encoding
*
* @stable ICU 3.4
*/
public CharsetDetector setDeclaredEncoding(String encoding) {
fDeclaredEncoding = encoding;
return this;
}
/**
* Set the input text (byte) data whose charset is to be detected.
*
* @param in the input text of unknown encoding
*
* @return This CharsetDetector
*
* @stable ICU 3.4
*/
public CharsetDetector setText(byte [] in) {
fRawInput = in;
fRawLength = in.length;
return this;
}
private static final int kBufSize = 8000;
/**
* Set the input text (byte) data whose charset is to be detected.
*
* The input stream that supplies the character data must have markSupported()
* == true; the charset detection process will read a small amount of data,
* then return the stream to its original position via
* the InputStream.reset() operation. The exact amount that will
* be read depends on the characteristics of the data itself.
*
* @param in the input text of unknown encoding
*
* @return This CharsetDetector
*
* @stable ICU 3.4
*/
public CharsetDetector setText(InputStream in) throws IOException {
fInputStream = in;
fInputStream.mark(kBufSize);
fRawInput = new byte[kBufSize]; // Always make a new buffer because the
// previous one may have come from the caller,
// in which case we can't touch it.
fRawLength = 0;
int remainingLength = kBufSize;
while (remainingLength > 0 ) {
// read() may give data in smallish chunks, esp. for remote sources. Hence, this loop.
int bytesRead = fInputStream.read(fRawInput, fRawLength, remainingLength);
if (bytesRead <= 0) {
break;
}
fRawLength += bytesRead;
remainingLength -= bytesRead;
}
fInputStream.reset();
return this;
}
/**
* Return the charset that best matches the supplied input data.
*
* Note though, that because the detection
* only looks at the start of the input data,
* there is a possibility that the returned charset will fail to handle
* the full set of input data.
*
* Raise an exception if
*
* no charset appears to match the data.
* no input text has been provided
*
*
* @return a CharsetMatch object representing the best matching charset, or
* null if there are no matches.
*
* @stable ICU 3.4
*/
public CharsetMatch detect() {
// TODO: A better implementation would be to copy the detect loop from
// detectAll(), and cut it short as soon as a match with a high confidence
// is found. This is something to be done later, after things are otherwise
// working.
CharsetMatch matches[] = detectAll();
if (matches == null || matches.length == 0) {
return null;
}
return matches[0];
}
/**
* Return an array of all charsets that appear to be plausible
* matches with the input data. The array is ordered with the
* best quality match first.
*
* Raise an exception if
*
* no charsets appear to match the input data.
* no input text has been provided
*
*
* @return An array of CharsetMatch objects representing possibly matching charsets.
*
* @stable ICU 3.4
*/
public CharsetMatch[] detectAll() {
ArrayList matches = new ArrayList();
MungeInput(); // Strip html markup, collect byte stats.
// Iterate over all possible charsets, remember all that
// give a match quality > 0.
for (int i = 0; i < ALL_CS_RECOGNIZERS.size(); i++) {
CSRecognizerInfo rcinfo = ALL_CS_RECOGNIZERS.get(i);
boolean active = (fEnabledRecognizers != null) ? fEnabledRecognizers[i] : rcinfo.isDefaultEnabled;
if (active) {
CharsetMatch m = rcinfo.recognizer.match(this);
if (m != null) {
matches.add(m);
}
}
}
Collections.sort(matches); // CharsetMatch compares on confidence
Collections.reverse(matches); // Put best match first.
CharsetMatch [] resultArray = new CharsetMatch[matches.size()];
resultArray = matches.toArray(resultArray);
return resultArray;
}
/**
* Autodetect the charset of an inputStream, and return a Java Reader
* to access the converted input data.
*
* This is a convenience method that is equivalent to
* this.setDeclaredEncoding(declaredEncoding).setText(in).detect().getReader();
*
* For the input stream that supplies the character data, markSupported()
* must be true; the charset detection will read a small amount of data,
* then return the stream to its original position via
* the InputStream.reset() operation. The exact amount that will
* be read depends on the characteristics of the data itself.
*
* Raise an exception if no charsets appear to match the input data.
*
* @param in The source of the byte data in the unknown charset.
*
* @param declaredEncoding A declared encoding for the data, if available,
* or null or an empty string if none is available.
*
* @stable ICU 3.4
*/
public Reader getReader(InputStream in, String declaredEncoding) {
fDeclaredEncoding = declaredEncoding;
try {
setText(in);
CharsetMatch match = detect();
if (match == null) {
return null;
}
return match.getReader();
} catch (IOException e) {
return null;
}
}
/**
* Autodetect the charset of an inputStream, and return a String
* containing the converted input data.
*
* This is a convenience method that is equivalent to
* this.setDeclaredEncoding(declaredEncoding).setText(in).detect().getString();
*
* Raise an exception if no charsets appear to match the input data.
*
* @param in The source of the byte data in the unknown charset.
*
* @param declaredEncoding A declared encoding for the data, if available,
* or null or an empty string if none is available.
*
* @stable ICU 3.4
*/
public String getString(byte[] in, String declaredEncoding)
{
fDeclaredEncoding = declaredEncoding;
try {
setText(in);
CharsetMatch match = detect();
if (match == null) {
return null;
}
return match.getString(-1);
} catch (IOException e) {
return null;
}
}
/**
* Get the names of all charsets supported by CharsetDetector class.
*
* Note: Multiple different charset encodings in a same family may use
* a single shared name in this implementation. For example, this method returns
* an array including "ISO-8859-1" (ISO Latin 1), but not including "windows-1252"
* (Windows Latin 1). However, actual detection result could be "windows-1252"
* when the input data matches Latin 1 code points with any points only available
* in "windows-1252".
*
* @return an array of the names of all charsets supported by
* CharsetDetector class.
*
* @stable ICU 3.4
*/
public static String[] getAllDetectableCharsets() {
String[] allCharsetNames = new String[ALL_CS_RECOGNIZERS.size()];
for (int i = 0; i < allCharsetNames.length; i++) {
allCharsetNames[i] = ALL_CS_RECOGNIZERS.get(i).recognizer.getName();
}
return allCharsetNames;
}
/**
* Test whether or not input filtering is enabled.
*
* @return true if input text will be filtered.
*
* @see #enableInputFilter
*
* @stable ICU 3.4
*/
public boolean inputFilterEnabled()
{
return fStripTags;
}
/**
* Enable filtering of input text. If filtering is enabled,
* text within angle brackets ("<" and ">") will be removed
* before detection.
*
* @param filter true to enable input text filtering.
*
* @return The previous setting.
*
* @stable ICU 3.4
*/
public boolean enableInputFilter(boolean filter)
{
boolean previous = fStripTags;
fStripTags = filter;
return previous;
}
/*
* MungeInput - after getting a set of raw input data to be analyzed, preprocess
* it by removing what appears to be html markup.
*/
private void MungeInput() {
int srci = 0;
int dsti = 0;
byte b;
boolean inMarkup = false;
int openTags = 0;
int badTags = 0;
//
// html / xml markup stripping.
// quick and dirty, not 100% accurate, but hopefully good enough, statistically.
// discard everything within < brackets >
// Count how many total '<' and illegal (nested) '<' occur, so we can make some
// guess as to whether the input was actually marked up at all.
if (fStripTags) {
for (srci = 0; srci < fRawLength && dsti < fInputBytes.length; srci++) {
b = fRawInput[srci];
if (b == (byte)'<') {
if (inMarkup) {
badTags++;
}
inMarkup = true;
openTags++;
}
if (! inMarkup) {
fInputBytes[dsti++] = b;
}
if (b == (byte)'>') {
inMarkup = false;
}
}
fInputLen = dsti;
}
//
// If it looks like this input wasn't marked up, or if it looks like it's
// essentially nothing but markup abandon the markup stripping.
// Detection will have to work on the unstripped input.
//
if (openTags<5 || openTags/5 < badTags ||
(fInputLen < 100 && fRawLength>600)) {
int limit = fRawLength;
if (limit > kBufSize) {
limit = kBufSize;
}
for (srci=0; srci ALL_CS_RECOGNIZERS;
static {
List list = new ArrayList();
list.add(new CSRecognizerInfo(new CharsetRecog_UTF8(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_Unicode.CharsetRecog_UTF_16_BE(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_Unicode.CharsetRecog_UTF_16_LE(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_Unicode.CharsetRecog_UTF_32_BE(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_Unicode.CharsetRecog_UTF_32_LE(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_mbcs.CharsetRecog_sjis(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_2022.CharsetRecog_2022JP(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_2022.CharsetRecog_2022CN(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_2022.CharsetRecog_2022KR(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_mbcs.CharsetRecog_euc.CharsetRecog_gb_18030(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_mbcs.CharsetRecog_euc.CharsetRecog_euc_jp(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_mbcs.CharsetRecog_euc.CharsetRecog_euc_kr(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_mbcs.CharsetRecog_big5(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_8859_1(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_8859_2(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_8859_5_ru(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_8859_6_ar(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_8859_7_el(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_8859_8_I_he(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_8859_8_he(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_windows_1251(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_windows_1256(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_KOI8_R(), true));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_8859_9_tr(), true));
// IBM 420/424 recognizers are disabled by default
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_IBM424_he_rtl(), false));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_IBM424_he_ltr(), false));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_IBM420_ar_rtl(), false));
list.add(new CSRecognizerInfo(new CharsetRecog_sbcs.CharsetRecog_IBM420_ar_ltr(), false));
ALL_CS_RECOGNIZERS = Collections.unmodifiableList(list);
}
/**
* Get the names of charsets that can be recognized by this CharsetDetector instance.
*
* @return an array of the names of charsets that can be recognized by this CharsetDetector
* instance.
*
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public String[] getDetectableCharsets() {
List csnames = new ArrayList(ALL_CS_RECOGNIZERS.size());
for (int i = 0; i < ALL_CS_RECOGNIZERS.size(); i++) {
CSRecognizerInfo rcinfo = ALL_CS_RECOGNIZERS.get(i);
boolean active = (fEnabledRecognizers == null) ? rcinfo.isDefaultEnabled : fEnabledRecognizers[i];
if (active) {
csnames.add(rcinfo.recognizer.getName());
}
}
return csnames.toArray(new String[csnames.size()]);
}
/**
* Enable or disable individual charset encoding.
* A name of charset encoding must be included in the names returned by
* {@link #getAllDetectableCharsets()}.
*
* @param encoding the name of charset encoding.
* @param enabled true to enable, or false to disable the
* charset encoding.
* @return A reference to this CharsetDetector.
* @throws IllegalArgumentException when the name of charset encoding is
* not supported.
*
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
public CharsetDetector setDetectableCharset(String encoding, boolean enabled) {
int modIdx = -1;
boolean isDefaultVal = false;
for (int i = 0; i < ALL_CS_RECOGNIZERS.size(); i++) {
CSRecognizerInfo csrinfo = ALL_CS_RECOGNIZERS.get(i);
if (csrinfo.recognizer.getName().equals(encoding)) {
modIdx = i;
isDefaultVal = (csrinfo.isDefaultEnabled == enabled);
break;
}
}
if (modIdx < 0) {
// No matching encoding found
throw new IllegalArgumentException("Invalid encoding: " + "\"" + encoding + "\"");
}
if (fEnabledRecognizers == null && !isDefaultVal) {
// Create an array storing the non default setting
fEnabledRecognizers = new boolean[ALL_CS_RECOGNIZERS.size()];
// Initialize the array with default info
for (int i = 0; i < ALL_CS_RECOGNIZERS.size(); i++) {
fEnabledRecognizers[i] = ALL_CS_RECOGNIZERS.get(i).isDefaultEnabled;
}
}
if (fEnabledRecognizers != null) {
fEnabledRecognizers[modIdx] = enabled;
}
return this;
}
}
================================================
FILE: src/main/java/com/ibm/icu/text/CharsetMatch.java
================================================
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/**
*******************************************************************************
* Copyright (C) 2005-2016, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.text;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
/**
* This class represents a charset that has been identified by a CharsetDetector
* as a possible encoding for a set of input data. From an instance of this
* class, you can ask for a confidence level in the charset identification,
* or for Java Reader or String to access the original byte data in Unicode form.
*
* Instances of this class are created only by CharsetDetectors.
*
* Note: this class has a natural ordering that is inconsistent with equals.
* The natural ordering is based on the match confidence value.
*
* @stable ICU 3.4
*/
public class CharsetMatch implements Comparable {
/**
* Create a java.io.Reader for reading the Unicode character data corresponding
* to the original byte data supplied to the Charset detect operation.
*
* CAUTION: if the source of the byte data was an InputStream, a Reader
* can be created for only one matching char set using this method. If more
* than one charset needs to be tried, the caller will need to reset
* the InputStream and create InputStreamReaders itself, based on the charset name.
*
* @return the Reader for the Unicode character data.
*
* @stable ICU 3.4
*/
public Reader getReader() {
InputStream inputStream = fInputStream;
if (inputStream == null) {
inputStream = new ByteArrayInputStream(fRawInput, 0, fRawLength);
}
try {
inputStream.reset();
return new InputStreamReader(inputStream, getName());
} catch (IOException e) {
return null;
}
}
/**
* Create a Java String from Unicode character data corresponding
* to the original byte data supplied to the Charset detect operation.
*
* @return a String created from the converted input data.
*
* @stable ICU 3.4
*/
public String getString() throws java.io.IOException {
return getString(-1);
}
/**
* Create a Java String from Unicode character data corresponding
* to the original byte data supplied to the Charset detect operation.
* The length of the returned string is limited to the specified size;
* the string will be trunctated to this length if necessary. A limit value of
* zero or less is ignored, and treated as no limit.
*
* @param maxLength The maximum length of the String to be created when the
* source of the data is an input stream, or -1 for
* unlimited length.
* @return a String created from the converted input data.
*
* @stable ICU 3.4
*/
public String getString(int maxLength) throws java.io.IOException {
String result = null;
if (fInputStream != null) {
StringBuilder sb = new StringBuilder();
char[] buffer = new char[1024];
Reader reader = getReader();
int max = maxLength < 0? Integer.MAX_VALUE : maxLength;
int bytesRead = 0;
while ((bytesRead = reader.read(buffer, 0, Math.min(max, 1024))) >= 0) {
sb.append(buffer, 0, bytesRead);
max -= bytesRead;
}
reader.close();
return sb.toString();
} else {
String name = getName();
/*
* getName() may return a name with a suffix 'rtl' or 'ltr'. This cannot
* be used to open a charset (e.g. IBM424_rtl). The ending '_rtl' or 'ltr'
* should be stripped off before creating the string.
*/
int startSuffix = name.indexOf("_rtl") < 0 ? name.indexOf("_ltr") : name.indexOf("_rtl");
if (startSuffix > 0) {
name = name.substring(0, startSuffix);
}
result = new String(fRawInput, name);
}
return result;
}
/**
* Get an indication of the confidence in the charset detected.
* Confidence values range from 0-100, with larger numbers indicating
* a better match of the input data to the characteristics of the
* charset.
*
* @return the confidence in the charset match
*
* @stable ICU 3.4
*/
public int getConfidence() {
return fConfidence;
}
/**
* Get the name of the detected charset.
* The name will be one that can be used with other APIs on the
* platform that accept charset names. It is the "Canonical name"
* as defined by the class java.nio.charset.Charset; for
* charsets that are registered with the IANA charset registry,
* this is the MIME-preferred registerd name.
*
* @see java.nio.charset.Charset
* @see java.io.InputStreamReader
*
* @return The name of the charset.
*
* @stable ICU 3.4
*/
public String getName() {
return fCharsetName;
}
/**
* Get the ISO code for the language of the detected charset.
*
* @return The ISO code for the language or null if the language cannot be determined.
*
* @stable ICU 3.4
*/
public String getLanguage() {
return fLang;
}
/**
* Compare to other CharsetMatch objects.
* Comparison is based on the match confidence value, which
* allows CharsetDetector.detectAll() to order its results.
*
* @param other the CharsetMatch object to compare against.
* @return a negative integer, zero, or a positive integer as the
* confidence level of this CharsetMatch
* is less than, equal to, or greater than that of
* the argument.
* @throws ClassCastException if the argument is not a CharsetMatch.
* @stable ICU 4.4
*/
@Override
public int compareTo (CharsetMatch other) {
int compareResult = 0;
if (this.fConfidence > other.fConfidence) {
compareResult = 1;
} else if (this.fConfidence < other.fConfidence) {
compareResult = -1;
}
return compareResult;
}
/*
* Constructor. Implementation internal
*/
CharsetMatch(CharsetDetector det, CharsetRecognizer rec, int conf) {
fConfidence = conf;
// The references to the original application input data must be copied out
// of the charset recognizer to here, in case the application resets the
// recognizer before using this CharsetMatch.
if (det.fInputStream == null) {
// We only want the existing input byte data if it came straight from the user,
// not if is just the head of a stream.
fRawInput = det.fRawInput;
fRawLength = det.fRawLength;
}
fInputStream = det.fInputStream;
fCharsetName = rec.getName();
fLang = rec.getLanguage();
}
/*
* Constructor. Implementation internal
*/
CharsetMatch(CharsetDetector det, CharsetRecognizer rec, int conf, String csName, String lang) {
fConfidence = conf;
// The references to the original application input data must be copied out
// of the charset recognizer to here, in case the application resets the
// recognizer before using this CharsetMatch.
if (det.fInputStream == null) {
// We only want the existing input byte data if it came straight from the user,
// not if is just the head of a stream.
fRawInput = det.fRawInput;
fRawLength = det.fRawLength;
}
fInputStream = det.fInputStream;
fCharsetName = csName;
fLang = lang;
}
//
// Private Data
//
private int fConfidence;
private byte[] fRawInput = null; // Original, untouched input bytes.
// If user gave us a byte array, this is it.
private int fRawLength; // Length of data in fRawInput array.
private InputStream fInputStream = null; // User's input stream, or null if the user
// gave us a byte array.
private String fCharsetName; // The name of the charset this CharsetMatch
// represents. Filled in by the recognizer.
private String fLang; // The language, if one was determined by
// the recognizer during the detect operation.
}
================================================
FILE: src/main/java/com/ibm/icu/text/CharsetRecog_2022.java
================================================
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2005 - 2012, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.text;
/**
* class CharsetRecog_2022 part of the ICU charset detection implementation.
* This is a superclass for the individual detectors for
* each of the detectable members of the ISO 2022 family
* of encodings.
*
* The separate classes are nested within this class.
*/
abstract class CharsetRecog_2022 extends CharsetRecognizer {
/**
* Matching function shared among the 2022 detectors JP, CN and KR
* Counts up the number of legal an unrecognized escape sequences in
* the sample of text, and computes a score based on the total number &
* the proportion that fit the encoding.
*
*
* @param text the byte buffer containing text to analyse
* @param textLen the size of the text in the byte.
* @param escapeSequences the byte escape sequences to test for.
* @return match quality, in the range of 0-100.
*/
int match(byte [] text, int textLen, byte [][] escapeSequences) {
int i, j;
int escN;
int hits = 0;
int misses = 0;
int shifts = 0;
int quality;
scanInput:
for (i=0; i= 3 &&
(input[0] & 0xFF) == 0xef && (input[1] & 0xFF) == 0xbb && (input[2] & 0xFF) == 0xbf) {
hasBOM = true;
}
// Scan for multi-byte sequences
for (i=0; i=det.fRawLength) {
break;
}
b = input[i];
if ((b & 0xc0) != 0x080) {
numInvalid++;
break;
}
if (--trailBytes == 0) {
numValid++;
break;
}
}
}
// Cook up some sort of confidence score, based on presence of a BOM
// and the existence of valid and/or invalid multi-byte sequences.
confidence = 0;
if (hasBOM && numInvalid==0) {
confidence = 100;
} else if (hasBOM && numValid > numInvalid*10) {
confidence = 80;
} else if (numValid > 3 && numInvalid == 0) {
confidence = 100;
} else if (numValid > 0 && numInvalid == 0) {
confidence = 80;
} else if (numValid == 0 && numInvalid == 0) {
// Plain ASCII. Confidence must be > 10, it's more likely than UTF-16, which
// accepts ASCII with confidence = 10.
// TODO: add plain ASCII as an explicitly detected type.
confidence = 15;
} else if (numValid > numInvalid*10) {
// Probably corrupt utf-8 data. Valid sequences aren't likely by chance.
confidence = 25;
}
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
}
================================================
FILE: src/main/java/com/ibm/icu/text/CharsetRecog_Unicode.java
================================================
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 1996-2013, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
*/
package com.ibm.icu.text;
/**
* This class matches UTF-16 and UTF-32, both big- and little-endian. The
* BOM will be used if it is present.
*/
abstract class CharsetRecog_Unicode extends CharsetRecognizer {
/* (non-Javadoc)
* @see com.ibm.icu.text.CharsetRecognizer#getName()
*/
@Override
abstract String getName();
/* (non-Javadoc)
* @see com.ibm.icu.text.CharsetRecognizer#match(com.ibm.icu.text.CharsetDetector)
*/
@Override
abstract CharsetMatch match(CharsetDetector det);
static int codeUnit16FromBytes(byte hi, byte lo) {
return ((hi & 0xff) << 8) | (lo & 0xff);
}
// UTF-16 confidence calculation. Very simple minded, but better than nothing.
// Any 8 bit non-control characters bump the confidence up. These have a zero high byte,
// and are very likely to be UTF-16, although they could also be part of a UTF-32 code.
// NULs are a contra-indication, they will appear commonly if the actual encoding is UTF-32.
// NULs should be rare in actual text.
static int adjustConfidence(int codeUnit, int confidence) {
if (codeUnit == 0) {
confidence -= 10;
} else if ((codeUnit >= 0x20 && codeUnit <= 0xff) || codeUnit == 0x0a) {
confidence += 10;
}
if (confidence < 0) {
confidence = 0;
} else if (confidence > 100) {
confidence = 100;
}
return confidence;
}
static class CharsetRecog_UTF_16_BE extends CharsetRecog_Unicode
{
@Override
String getName()
{
return "UTF-16BE";
}
@Override
CharsetMatch match(CharsetDetector det)
{
byte[] input = det.fRawInput;
int confidence = 10;
int bytesToCheck = Math.min(input.length, 30);
for (int charIndex=0; charIndex 0) {
return new CharsetMatch(det, this, confidence);
}
return null;
}
}
static class CharsetRecog_UTF_16_LE extends CharsetRecog_Unicode
{
@Override
String getName()
{
return "UTF-16LE";
}
@Override
CharsetMatch match(CharsetDetector det)
{
byte[] input = det.fRawInput;
int confidence = 10;
int bytesToCheck = Math.min(input.length, 30);
for (int charIndex=0; charIndex 0) {
return new CharsetMatch(det, this, confidence);
}
return null;
}
}
static abstract class CharsetRecog_UTF_32 extends CharsetRecog_Unicode
{
abstract int getChar(byte[] input, int index);
@Override
abstract String getName();
@Override
CharsetMatch match(CharsetDetector det)
{
byte[] input = det.fRawInput;
int limit = (det.fRawLength / 4) * 4;
int numValid = 0;
int numInvalid = 0;
boolean hasBOM = false;
int confidence = 0;
if (limit==0) {
return null;
}
if (getChar(input, 0) == 0x0000FEFF) {
hasBOM = true;
}
for(int i = 0; i < limit; i += 4) {
int ch = getChar(input, i);
if (ch < 0 || ch >= 0x10FFFF || (ch >= 0xD800 && ch <= 0xDFFF)) {
numInvalid += 1;
} else {
numValid += 1;
}
}
// Cook up some sort of confidence score, based on presence of a BOM
// and the existence of valid and/or invalid multi-byte sequences.
if (hasBOM && numInvalid==0) {
confidence = 100;
} else if (hasBOM && numValid > numInvalid*10) {
confidence = 80;
} else if (numValid > 3 && numInvalid == 0) {
confidence = 100;
} else if (numValid > 0 && numInvalid == 0) {
confidence = 80;
} else if (numValid > numInvalid*10) {
// Probably corrupt UTF-32BE data. Valid sequences aren't likely by chance.
confidence = 25;
}
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
}
static class CharsetRecog_UTF_32_BE extends CharsetRecog_UTF_32
{
@Override
int getChar(byte[] input, int index)
{
return (input[index + 0] & 0xFF) << 24 | (input[index + 1] & 0xFF) << 16 |
(input[index + 2] & 0xFF) << 8 | (input[index + 3] & 0xFF);
}
@Override
String getName()
{
return "UTF-32BE";
}
}
static class CharsetRecog_UTF_32_LE extends CharsetRecog_UTF_32
{
@Override
int getChar(byte[] input, int index)
{
return (input[index + 3] & 0xFF) << 24 | (input[index + 2] & 0xFF) << 16 |
(input[index + 1] & 0xFF) << 8 | (input[index + 0] & 0xFF);
}
@Override
String getName()
{
return "UTF-32LE";
}
}
}
================================================
FILE: src/main/java/com/ibm/icu/text/CharsetRecog_mbcs.java
================================================
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
****************************************************************************
* Copyright (C) 2005-2012, International Business Machines Corporation and *
* others. All Rights Reserved. *
****************************************************************************
*
*/
package com.ibm.icu.text;
import java.util.Arrays;
/**
* CharsetRecognizer implementation for Asian - double or multi-byte - charsets.
* Match is determined mostly by the input data adhering to the
* encoding scheme for the charset, and, optionally,
* frequency-of-occurrence of characters.
*
* Instances of this class are singletons, one per encoding
* being recognized. They are created in the main
* CharsetDetector class and kept in the global list of available
* encodings to be checked. The specific encoding being recognized
* is determined by subclass.
*/
abstract class CharsetRecog_mbcs extends CharsetRecognizer {
/**
* Get the IANA name of this charset.
* @return the charset name.
*/
@Override
abstract String getName() ;
/**
* Test the match of this charset with the input text data
* which is obtained via the CharsetDetector object.
*
* @param det The CharsetDetector, which contains the input text
* to be checked for being in this charset.
* @return Two values packed into one int (Damn java, anyhow)
*
* bits 0-7: the match confidence, ranging from 0-100
*
* bits 8-15: The match reason, an enum-like value.
*/
int match(CharsetDetector det, int [] commonChars) {
@SuppressWarnings("unused")
int singleByteCharCount = 0; //TODO Do we really need this?
int doubleByteCharCount = 0;
int commonCharCount = 0;
int badCharCount = 0;
int totalCharCount = 0;
int confidence = 0;
iteratedChar iter = new iteratedChar();
detectBlock: {
for (iter.reset(); nextChar(iter, det);) {
totalCharCount++;
if (iter.error) {
badCharCount++;
} else {
long cv = iter.charValue & 0xFFFFFFFFL;
if (cv <= 0xff) {
singleByteCharCount++;
} else {
doubleByteCharCount++;
if (commonChars != null) {
// NOTE: This assumes that there are no 4-byte common chars.
if (Arrays.binarySearch(commonChars, (int) cv) >= 0) {
commonCharCount++;
}
}
}
}
if (badCharCount >= 2 && badCharCount*5 >= doubleByteCharCount) {
// Bail out early if the byte data is not matching the encoding scheme.
break detectBlock;
}
}
if (doubleByteCharCount <= 10 && badCharCount== 0) {
// Not many multi-byte chars.
if (doubleByteCharCount == 0 && totalCharCount < 10) {
// There weren't any multibyte sequences, and there was a low density of non-ASCII single bytes.
// We don't have enough data to have any confidence.
// Statistical analysis of single byte non-ASCII characters would probably help here.
confidence = 0;
}
else {
// ASCII or ISO file? It's probably not our encoding,
// but is not incompatible with our encoding, so don't give it a zero.
confidence = 10;
}
break detectBlock;
}
//
// No match if there are too many characters that don't fit the encoding scheme.
// (should we have zero tolerance for these?)
//
if (doubleByteCharCount < 20*badCharCount) {
confidence = 0;
break detectBlock;
}
if (commonChars == null) {
// We have no statistics on frequently occurring characters.
// Assess confidence purely on having a reasonable number of
// multi-byte characters (the more the better
confidence = 30 + doubleByteCharCount - 20*badCharCount;
if (confidence > 100) {
confidence = 100;
}
}else {
//
// Frequency of occurrence statistics exist.
//
double maxVal = Math.log((float)doubleByteCharCount / 4);
double scaleFactor = 90.0 / maxVal;
confidence = (int)(Math.log(commonCharCount+1) * scaleFactor + 10);
confidence = Math.min(confidence, 100);
}
} // end of detectBlock:
return confidence;
}
// "Character" iterated character class.
// Recognizers for specific mbcs encodings make their "characters" available
// by providing a nextChar() function that fills in an instance of iteratedChar
// with the next char from the input.
// The returned characters are not converted to Unicode, but remain as the raw
// bytes (concatenated into an int) from the codepage data.
//
// For Asian charsets, use the raw input rather than the input that has been
// stripped of markup. Detection only considers multi-byte chars, effectively
// stripping markup anyway, and double byte chars do occur in markup too.
//
static class iteratedChar {
int charValue = 0; // 1-4 bytes from the raw input data
int nextIndex = 0;
boolean error = false;
boolean done = false;
void reset() {
charValue = 0;
nextIndex = 0;
error = false;
done = false;
}
int nextByte(CharsetDetector det) {
if (nextIndex >= det.fRawLength) {
done = true;
return -1;
}
int byteValue = det.fRawInput[nextIndex++] & 0x00ff;
return byteValue;
}
}
/**
* Get the next character (however many bytes it is) from the input data
* Subclasses for specific charset encodings must implement this function
* to get characters according to the rules of their encoding scheme.
*
* This function is not a method of class iteratedChar only because
* that would require a lot of extra derived classes, which is awkward.
* @param it The iteratedChar "struct" into which the returned char is placed.
* @param det The charset detector, which is needed to get at the input byte data
* being iterated over.
* @return True if a character was returned, false at end of input.
*/
abstract boolean nextChar(iteratedChar it, CharsetDetector det);
/**
* Shift-JIS charset recognizer.
*
*/
static class CharsetRecog_sjis extends CharsetRecog_mbcs {
static int [] commonChars =
// TODO: This set of data comes from the character frequency-
// of-occurrence analysis tool. The data needs to be moved
// into a resource and loaded from there.
{0x8140, 0x8141, 0x8142, 0x8145, 0x815b, 0x8169, 0x816a, 0x8175, 0x8176, 0x82a0,
0x82a2, 0x82a4, 0x82a9, 0x82aa, 0x82ab, 0x82ad, 0x82af, 0x82b1, 0x82b3, 0x82b5,
0x82b7, 0x82bd, 0x82be, 0x82c1, 0x82c4, 0x82c5, 0x82c6, 0x82c8, 0x82c9, 0x82cc,
0x82cd, 0x82dc, 0x82e0, 0x82e7, 0x82e8, 0x82e9, 0x82ea, 0x82f0, 0x82f1, 0x8341,
0x8343, 0x834e, 0x834f, 0x8358, 0x835e, 0x8362, 0x8367, 0x8375, 0x8376, 0x8389,
0x838a, 0x838b, 0x838d, 0x8393, 0x8e96, 0x93fa, 0x95aa};
@Override
boolean nextChar(iteratedChar it, CharsetDetector det) {
it.error = false;
int firstByte;
firstByte = it.charValue = it.nextByte(det);
if (firstByte < 0) {
return false;
}
if (firstByte <= 0x7f || (firstByte>0xa0 && firstByte<=0xdf)) {
return true;
}
int secondByte = it.nextByte(det);
if (secondByte < 0) {
return false;
}
it.charValue = (firstByte << 8) | secondByte;
if (! ((secondByte>=0x40 && secondByte<=0x7f) || (secondByte>=0x80 && secondByte<=0xff))) {
// Illegal second byte value.
it.error = true;
}
return true;
}
@Override
CharsetMatch match(CharsetDetector det) {
int confidence = match(det, commonChars);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
@Override
String getName() {
return "Shift_JIS";
}
@Override
public String getLanguage()
{
return "ja";
}
}
/**
* Big5 charset recognizer.
*
*/
static class CharsetRecog_big5 extends CharsetRecog_mbcs {
static int [] commonChars =
// TODO: This set of data comes from the character frequency-
// of-occurrence analysis tool. The data needs to be moved
// into a resource and loaded from there.
{0xa140, 0xa141, 0xa142, 0xa143, 0xa147, 0xa149, 0xa175, 0xa176, 0xa440, 0xa446,
0xa447, 0xa448, 0xa451, 0xa454, 0xa457, 0xa464, 0xa46a, 0xa46c, 0xa477, 0xa4a3,
0xa4a4, 0xa4a7, 0xa4c1, 0xa4ce, 0xa4d1, 0xa4df, 0xa4e8, 0xa4fd, 0xa540, 0xa548,
0xa558, 0xa569, 0xa5cd, 0xa5e7, 0xa657, 0xa661, 0xa662, 0xa668, 0xa670, 0xa6a8,
0xa6b3, 0xa6b9, 0xa6d3, 0xa6db, 0xa6e6, 0xa6f2, 0xa740, 0xa751, 0xa759, 0xa7da,
0xa8a3, 0xa8a5, 0xa8ad, 0xa8d1, 0xa8d3, 0xa8e4, 0xa8fc, 0xa9c0, 0xa9d2, 0xa9f3,
0xaa6b, 0xaaba, 0xaabe, 0xaacc, 0xaafc, 0xac47, 0xac4f, 0xacb0, 0xacd2, 0xad59,
0xaec9, 0xafe0, 0xb0ea, 0xb16f, 0xb2b3, 0xb2c4, 0xb36f, 0xb44c, 0xb44e, 0xb54c,
0xb5a5, 0xb5bd, 0xb5d0, 0xb5d8, 0xb671, 0xb7ed, 0xb867, 0xb944, 0xbad8, 0xbb44,
0xbba1, 0xbdd1, 0xc2c4, 0xc3b9, 0xc440, 0xc45f};
@Override
boolean nextChar(iteratedChar it, CharsetDetector det) {
it.error = false;
int firstByte;
firstByte = it.charValue = it.nextByte(det);
if (firstByte < 0) {
return false;
}
if (firstByte <= 0x7f || firstByte==0xff) {
// single byte character.
return true;
}
int secondByte = it.nextByte(det);
if (secondByte < 0) {
return false;
}
it.charValue = (it.charValue << 8) | secondByte;
if (secondByte < 0x40 ||
secondByte ==0x7f ||
secondByte == 0xff) {
it.error = true;
}
return true;
}
@Override
CharsetMatch match(CharsetDetector det) {
int confidence = match(det, commonChars);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
@Override
String getName() {
return "Big5";
}
@Override
public String getLanguage()
{
return "zh";
}
}
/**
* EUC charset recognizers. One abstract class that provides the common function
* for getting the next character according to the EUC encoding scheme,
* and nested derived classes for EUC_KR, EUC_JP, EUC_CN.
*
*/
abstract static class CharsetRecog_euc extends CharsetRecog_mbcs {
/*
* (non-Javadoc)
* Get the next character value for EUC based encodings.
* Character "value" is simply the raw bytes that make up the character
* packed into an int.
*/
@Override
boolean nextChar(iteratedChar it, CharsetDetector det) {
it.error = false;
int firstByte = 0;
int secondByte = 0;
int thirdByte = 0;
//int fourthByte = 0;
buildChar: {
firstByte = it.charValue = it.nextByte(det);
if (firstByte < 0) {
// Ran off the end of the input data
it.done = true;
break buildChar;
}
if (firstByte <= 0x8d) {
// single byte char
break buildChar;
}
secondByte = it.nextByte(det);
it.charValue = (it.charValue << 8) | secondByte;
if (firstByte >= 0xA1 && firstByte <= 0xfe) {
// Two byte Char
if (secondByte < 0xa1) {
it.error = true;
}
break buildChar;
}
if (firstByte == 0x8e) {
// Code Set 2.
// In EUC-JP, total char size is 2 bytes, only one byte of actual char value.
// In EUC-TW, total char size is 4 bytes, three bytes contribute to char value.
// We don't know which we've got.
// Treat it like EUC-JP. If the data really was EUC-TW, the following two
// bytes will look like a well formed 2 byte char.
if (secondByte < 0xa1) {
it.error = true;
}
break buildChar;
}
if (firstByte == 0x8f) {
// Code set 3.
// Three byte total char size, two bytes of actual char value.
thirdByte = it.nextByte(det);
it.charValue = (it.charValue << 8) | thirdByte;
if (thirdByte < 0xa1) {
it.error = true;
}
}
}
return (it.done == false);
}
/**
* The charset recognize for EUC-JP. A singleton instance of this class
* is created and kept by the public CharsetDetector class
*/
static class CharsetRecog_euc_jp extends CharsetRecog_euc {
static int [] commonChars =
// TODO: This set of data comes from the character frequency-
// of-occurrence analysis tool. The data needs to be moved
// into a resource and loaded from there.
{0xa1a1, 0xa1a2, 0xa1a3, 0xa1a6, 0xa1bc, 0xa1ca, 0xa1cb, 0xa1d6, 0xa1d7, 0xa4a2,
0xa4a4, 0xa4a6, 0xa4a8, 0xa4aa, 0xa4ab, 0xa4ac, 0xa4ad, 0xa4af, 0xa4b1, 0xa4b3,
0xa4b5, 0xa4b7, 0xa4b9, 0xa4bb, 0xa4bd, 0xa4bf, 0xa4c0, 0xa4c1, 0xa4c3, 0xa4c4,
0xa4c6, 0xa4c7, 0xa4c8, 0xa4c9, 0xa4ca, 0xa4cb, 0xa4ce, 0xa4cf, 0xa4d0, 0xa4de,
0xa4df, 0xa4e1, 0xa4e2, 0xa4e4, 0xa4e8, 0xa4e9, 0xa4ea, 0xa4eb, 0xa4ec, 0xa4ef,
0xa4f2, 0xa4f3, 0xa5a2, 0xa5a3, 0xa5a4, 0xa5a6, 0xa5a7, 0xa5aa, 0xa5ad, 0xa5af,
0xa5b0, 0xa5b3, 0xa5b5, 0xa5b7, 0xa5b8, 0xa5b9, 0xa5bf, 0xa5c3, 0xa5c6, 0xa5c7,
0xa5c8, 0xa5c9, 0xa5cb, 0xa5d0, 0xa5d5, 0xa5d6, 0xa5d7, 0xa5de, 0xa5e0, 0xa5e1,
0xa5e5, 0xa5e9, 0xa5ea, 0xa5eb, 0xa5ec, 0xa5ed, 0xa5f3, 0xb8a9, 0xb9d4, 0xbaee,
0xbbc8, 0xbef0, 0xbfb7, 0xc4ea, 0xc6fc, 0xc7bd, 0xcab8, 0xcaf3, 0xcbdc, 0xcdd1};
@Override
String getName() {
return "EUC-JP";
}
@Override
CharsetMatch match(CharsetDetector det) {
int confidence = match(det, commonChars);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
@Override
public String getLanguage()
{
return "ja";
}
}
/**
* The charset recognize for EUC-KR. A singleton instance of this class
* is created and kept by the public CharsetDetector class
*/
static class CharsetRecog_euc_kr extends CharsetRecog_euc {
static int [] commonChars =
// TODO: This set of data comes from the character frequency-
// of-occurrence analysis tool. The data needs to be moved
// into a resource and loaded from there.
{0xb0a1, 0xb0b3, 0xb0c5, 0xb0cd, 0xb0d4, 0xb0e6, 0xb0ed, 0xb0f8, 0xb0fa, 0xb0fc,
0xb1b8, 0xb1b9, 0xb1c7, 0xb1d7, 0xb1e2, 0xb3aa, 0xb3bb, 0xb4c2, 0xb4cf, 0xb4d9,
0xb4eb, 0xb5a5, 0xb5b5, 0xb5bf, 0xb5c7, 0xb5e9, 0xb6f3, 0xb7af, 0xb7c2, 0xb7ce,
0xb8a6, 0xb8ae, 0xb8b6, 0xb8b8, 0xb8bb, 0xb8e9, 0xb9ab, 0xb9ae, 0xb9cc, 0xb9ce,
0xb9fd, 0xbab8, 0xbace, 0xbad0, 0xbaf1, 0xbbe7, 0xbbf3, 0xbbfd, 0xbcad, 0xbcba,
0xbcd2, 0xbcf6, 0xbdba, 0xbdc0, 0xbdc3, 0xbdc5, 0xbec6, 0xbec8, 0xbedf, 0xbeee,
0xbef8, 0xbefa, 0xbfa1, 0xbfa9, 0xbfc0, 0xbfe4, 0xbfeb, 0xbfec, 0xbff8, 0xc0a7,
0xc0af, 0xc0b8, 0xc0ba, 0xc0bb, 0xc0bd, 0xc0c7, 0xc0cc, 0xc0ce, 0xc0cf, 0xc0d6,
0xc0da, 0xc0e5, 0xc0fb, 0xc0fc, 0xc1a4, 0xc1a6, 0xc1b6, 0xc1d6, 0xc1df, 0xc1f6,
0xc1f8, 0xc4a1, 0xc5cd, 0xc6ae, 0xc7cf, 0xc7d1, 0xc7d2, 0xc7d8, 0xc7e5, 0xc8ad};
@Override
String getName() {
return "EUC-KR";
}
@Override
CharsetMatch match(CharsetDetector det) {
int confidence = match(det, commonChars);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
@Override
public String getLanguage()
{
return "ko";
}
}
}
/**
*
* GB-18030 recognizer. Uses simplified Chinese statistics.
*
*/
static class CharsetRecog_gb_18030 extends CharsetRecog_mbcs {
/*
* (non-Javadoc)
* Get the next character value for EUC based encodings.
* Character "value" is simply the raw bytes that make up the character
* packed into an int.
*/
@Override
boolean nextChar(iteratedChar it, CharsetDetector det) {
it.error = false;
int firstByte = 0;
int secondByte = 0;
int thirdByte = 0;
int fourthByte = 0;
buildChar: {
firstByte = it.charValue = it.nextByte(det);
if (firstByte < 0) {
// Ran off the end of the input data
it.done = true;
break buildChar;
}
if (firstByte <= 0x80) {
// single byte char
break buildChar;
}
secondByte = it.nextByte(det);
it.charValue = (it.charValue << 8) | secondByte;
if (firstByte >= 0x81 && firstByte <= 0xFE) {
// Two byte Char
if ((secondByte >= 0x40 && secondByte <= 0x7E) || (secondByte >=80 && secondByte <=0xFE)) {
break buildChar;
}
// Four byte char
if (secondByte >= 0x30 && secondByte <= 0x39) {
thirdByte = it.nextByte(det);
if (thirdByte >= 0x81 && thirdByte <= 0xFE) {
fourthByte = it.nextByte(det);
if (fourthByte >= 0x30 && fourthByte <= 0x39) {
it.charValue = (it.charValue << 16) | (thirdByte << 8) | fourthByte;
break buildChar;
}
}
}
it.error = true;
break buildChar;
}
}
return (it.done == false);
}
static int [] commonChars =
// TODO: This set of data comes from the character frequency-
// of-occurrence analysis tool. The data needs to be moved
// into a resource and loaded from there.
{0xa1a1, 0xa1a2, 0xa1a3, 0xa1a4, 0xa1b0, 0xa1b1, 0xa1f1, 0xa1f3, 0xa3a1, 0xa3ac,
0xa3ba, 0xb1a8, 0xb1b8, 0xb1be, 0xb2bb, 0xb3c9, 0xb3f6, 0xb4f3, 0xb5bd, 0xb5c4,
0xb5e3, 0xb6af, 0xb6d4, 0xb6e0, 0xb7a2, 0xb7a8, 0xb7bd, 0xb7d6, 0xb7dd, 0xb8b4,
0xb8df, 0xb8f6, 0xb9ab, 0xb9c9, 0xb9d8, 0xb9fa, 0xb9fd, 0xbacd, 0xbba7, 0xbbd6,
0xbbe1, 0xbbfa, 0xbcbc, 0xbcdb, 0xbcfe, 0xbdcc, 0xbecd, 0xbedd, 0xbfb4, 0xbfc6,
0xbfc9, 0xc0b4, 0xc0ed, 0xc1cb, 0xc2db, 0xc3c7, 0xc4dc, 0xc4ea, 0xc5cc, 0xc6f7,
0xc7f8, 0xc8ab, 0xc8cb, 0xc8d5, 0xc8e7, 0xc9cf, 0xc9fa, 0xcab1, 0xcab5, 0xcac7,
0xcad0, 0xcad6, 0xcaf5, 0xcafd, 0xccec, 0xcdf8, 0xceaa, 0xcec4, 0xced2, 0xcee5,
0xcfb5, 0xcfc2, 0xcfd6, 0xd0c2, 0xd0c5, 0xd0d0, 0xd0d4, 0xd1a7, 0xd2aa, 0xd2b2,
0xd2b5, 0xd2bb, 0xd2d4, 0xd3c3, 0xd3d0, 0xd3fd, 0xd4c2, 0xd4da, 0xd5e2, 0xd6d0};
@Override
String getName() {
return "GB18030";
}
@Override
CharsetMatch match(CharsetDetector det) {
int confidence = match(det, commonChars);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
@Override
public String getLanguage()
{
return "zh";
}
}
}
================================================
FILE: src/main/java/com/ibm/icu/text/CharsetRecog_sbcs.java
================================================
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
****************************************************************************
* Copyright (C) 2005-2013, International Business Machines Corporation and *
* others. All Rights Reserved. *
************************************************************************** *
*
*/
package com.ibm.icu.text;
/**
* This class recognizes single-byte encodings. Because the encoding scheme is so
* simple, language statistics are used to do the matching.
*/
abstract class CharsetRecog_sbcs extends CharsetRecognizer {
/* (non-Javadoc)
* @see com.ibm.icu.text.CharsetRecognizer#getName()
*/
@Override
abstract String getName();
static class NGramParser
{
// private static final int N_GRAM_SIZE = 3;
private static final int N_GRAM_MASK = 0xFFFFFF;
protected int byteIndex = 0;
private int ngram = 0;
private int[] ngramList;
protected byte[] byteMap;
private int ngramCount;
private int hitCount;
protected byte spaceChar;
public NGramParser(int[] theNgramList, byte[] theByteMap)
{
ngramList = theNgramList;
byteMap = theByteMap;
ngram = 0;
ngramCount = hitCount = 0;
}
/*
* Binary search for value in table, which must have exactly 64 entries.
*/
private static int search(int[] table, int value)
{
int index = 0;
if (table[index + 32] <= value) {
index += 32;
}
if (table[index + 16] <= value) {
index += 16;
}
if (table[index + 8] <= value) {
index += 8;
}
if (table[index + 4] <= value) {
index += 4;
}
if (table[index + 2] <= value) {
index += 2;
}
if (table[index + 1] <= value) {
index += 1;
}
if (table[index] > value) {
index -= 1;
}
if (index < 0 || table[index] != value) {
return -1;
}
return index;
}
private void lookup(int thisNgram)
{
ngramCount += 1;
if (search(ngramList, thisNgram) >= 0) {
hitCount += 1;
}
}
protected void addByte(int b)
{
ngram = ((ngram << 8) + (b & 0xFF)) & N_GRAM_MASK;
lookup(ngram);
}
private int nextByte(CharsetDetector det)
{
if (byteIndex >= det.fInputLen) {
return -1;
}
return det.fInputBytes[byteIndex++] & 0xFF;
}
protected void parseCharacters(CharsetDetector det)
{
int b;
boolean ignoreSpace = false;
while ((b = nextByte(det)) >= 0) {
byte mb = byteMap[b];
// TODO: 0x20 might not be a space in all character sets...
if (mb != 0) {
if (!(mb == spaceChar && ignoreSpace)) {
addByte(mb);
}
ignoreSpace = (mb == spaceChar);
}
}
}
public int parse(CharsetDetector det)
{
return parse (det, (byte)0x20);
}
public int parse(CharsetDetector det, byte spaceCh)
{
this.spaceChar = spaceCh;
parseCharacters(det);
// TODO: Is this OK? The buffer could have ended in the middle of a word...
addByte(spaceChar);
double rawPercent = (double) hitCount / (double) ngramCount;
// if (rawPercent <= 2.0) {
// return 0;
// }
// TODO - This is a bit of a hack to take care of a case
// were we were getting a confidence of 135...
if (rawPercent > 0.33) {
return 98;
}
return (int) (rawPercent * 300.0);
}
}
static class NGramParser_IBM420 extends NGramParser
{
private byte alef = 0x00;
protected static byte[] unshapeMap = {
/* -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F */
/* 0- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 1- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 2- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 3- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 4- */ (byte) 0x40, (byte) 0x40, (byte) 0x42, (byte) 0x42, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47, (byte) 0x47, (byte) 0x49, (byte) 0x4A, (byte) 0x4B, (byte) 0x4C, (byte) 0x4D, (byte) 0x4E, (byte) 0x4F,
/* 5- */ (byte) 0x50, (byte) 0x49, (byte) 0x52, (byte) 0x53, (byte) 0x54, (byte) 0x55, (byte) 0x56, (byte) 0x56, (byte) 0x58, (byte) 0x58, (byte) 0x5A, (byte) 0x5B, (byte) 0x5C, (byte) 0x5D, (byte) 0x5E, (byte) 0x5F,
/* 6- */ (byte) 0x60, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x63, (byte) 0x65, (byte) 0x65, (byte) 0x67, (byte) 0x67, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
/* 7- */ (byte) 0x69, (byte) 0x71, (byte) 0x71, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77, (byte) 0x77, (byte) 0x79, (byte) 0x7A, (byte) 0x7B, (byte) 0x7C, (byte) 0x7D, (byte) 0x7E, (byte) 0x7F,
/* 8- */ (byte) 0x80, (byte) 0x81, (byte) 0x82, (byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89, (byte) 0x80, (byte) 0x8B, (byte) 0x8B, (byte) 0x8D, (byte) 0x8D, (byte) 0x8F,
/* 9- */ (byte) 0x90, (byte) 0x91, (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98, (byte) 0x99, (byte) 0x9A, (byte) 0x9A, (byte) 0x9A, (byte) 0x9A, (byte) 0x9E, (byte) 0x9E,
/* A- */ (byte) 0x9E, (byte) 0xA1, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5, (byte) 0xA6, (byte) 0xA7, (byte) 0xA8, (byte) 0xA9, (byte) 0x9E, (byte) 0xAB, (byte) 0xAB, (byte) 0xAD, (byte) 0xAD, (byte) 0xAF,
/* B- */ (byte) 0xAF, (byte) 0xB1, (byte) 0xB2, (byte) 0xB3, (byte) 0xB4, (byte) 0xB5, (byte) 0xB6, (byte) 0xB7, (byte) 0xB8, (byte) 0xB9, (byte) 0xB1, (byte) 0xBB, (byte) 0xBB, (byte) 0xBD, (byte) 0xBD, (byte) 0xBF,
/* C- */ (byte) 0xC0, (byte) 0xC1, (byte) 0xC2, (byte) 0xC3, (byte) 0xC4, (byte) 0xC5, (byte) 0xC6, (byte) 0xC7, (byte) 0xC8, (byte) 0xC9, (byte) 0xCA, (byte) 0xBF, (byte) 0xCC, (byte) 0xBF, (byte) 0xCE, (byte) 0xCF,
/* D- */ (byte) 0xD0, (byte) 0xD1, (byte) 0xD2, (byte) 0xD3, (byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0xD7, (byte) 0xD8, (byte) 0xD9, (byte) 0xDA, (byte) 0xDA, (byte) 0xDC, (byte) 0xDC, (byte) 0xDC, (byte) 0xDF,
/* E- */ (byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7, (byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
/* F- */ (byte) 0xF0, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7, (byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xFC, (byte) 0xFD, (byte) 0xFE, (byte) 0xFF,
};
public NGramParser_IBM420(int[] theNgramList, byte[] theByteMap)
{
super(theNgramList, theByteMap);
}
private byte isLamAlef(byte b) {
if(b == (byte)0xb2 || b == (byte)0xb3){
return (byte)0x47;
}else if(b == (byte)0xb4 || b == (byte)0xb5){
return (byte)0x49;
}else if(b == (byte)0xb8 || b == (byte)0xb9){
return (byte)0x56;
}else
return (byte)0x00;
}
/*
* Arabic shaping needs to be done manually. Cannot call ArabicShaping class
* because CharsetDetector is dealing with bytes not Unicode code points. We could
* convert the bytes to Unicode code points but that would leave us dependent
* on CharsetICU which we try to avoid. IBM420 converter amongst different versions
* of JDK can produce different results and therefore is also avoided.
*/
private int nextByte(CharsetDetector det)
{
if (byteIndex >= det.fInputLen || det.fInputBytes[byteIndex] == 0) {
return -1;
}
int next;
alef = isLamAlef(det.fInputBytes[byteIndex]);
if(alef != (byte)0x00)
next = 0xB1 & 0xFF;
else
next = unshapeMap[det.fInputBytes[byteIndex]& 0xFF] & 0xFF;
byteIndex++;
return next;
}
@Override
protected void parseCharacters(CharsetDetector det)
{
int b;
boolean ignoreSpace = false;
while ((b = nextByte(det)) >= 0) {
byte mb = byteMap[b];
// TODO: 0x20 might not be a space in all character sets...
if (mb != 0) {
if (!(mb == spaceChar && ignoreSpace)) {
addByte(mb);
}
ignoreSpace = (mb == spaceChar);
}
if(alef != (byte)0x00){
mb = byteMap[alef & 0xFF];
// TODO: 0x20 might not be a space in all character sets...
if (mb != 0) {
if (!(mb == spaceChar && ignoreSpace)) {
addByte(mb);
}
ignoreSpace = (mb == spaceChar);
}
}
}
}
}
int match(CharsetDetector det, int[] ngrams, byte[] byteMap)
{
return match (det, ngrams, byteMap, (byte)0x20);
}
int match(CharsetDetector det, int[] ngrams, byte[] byteMap, byte spaceChar)
{
NGramParser parser = new NGramParser(ngrams, byteMap);
return parser.parse(det, spaceChar);
}
int matchIBM420(CharsetDetector det, int[] ngrams, byte[] byteMap, byte spaceChar){
NGramParser_IBM420 parser = new NGramParser_IBM420(ngrams, byteMap);
return parser.parse(det, spaceChar);
}
static class NGramsPlusLang {
int[] fNGrams;
String fLang;
NGramsPlusLang(String la, int [] ng) {
fLang = la;
fNGrams = ng;
}
}
static class CharsetRecog_8859_1 extends CharsetRecog_sbcs
{
protected static byte[] byteMap = {
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x00,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0xAA, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0xB5, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0xBA, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0xF0, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0x20,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xFC, (byte) 0xFD, (byte) 0xFE, (byte) 0xDF,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0xF0, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0x20,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xFC, (byte) 0xFD, (byte) 0xFE, (byte) 0xFF,
};
private static NGramsPlusLang[] ngrams_8859_1 = new NGramsPlusLang[] {
new NGramsPlusLang(
"da",
new int[] {
0x206166, 0x206174, 0x206465, 0x20656E, 0x206572, 0x20666F, 0x206861, 0x206920, 0x206D65, 0x206F67, 0x2070E5, 0x207369, 0x207374, 0x207469, 0x207669, 0x616620,
0x616E20, 0x616E64, 0x617220, 0x617420, 0x646520, 0x64656E, 0x646572, 0x646574, 0x652073, 0x656420, 0x656465, 0x656E20, 0x656E64, 0x657220, 0x657265, 0x657320,
0x657420, 0x666F72, 0x676520, 0x67656E, 0x676572, 0x696765, 0x696C20, 0x696E67, 0x6B6520, 0x6B6B65, 0x6C6572, 0x6C6967, 0x6C6C65, 0x6D6564, 0x6E6465, 0x6E6520,
0x6E6720, 0x6E6765, 0x6F6720, 0x6F6D20, 0x6F7220, 0x70E520, 0x722064, 0x722065, 0x722073, 0x726520, 0x737465, 0x742073, 0x746520, 0x746572, 0x74696C, 0x766572,
}),
new NGramsPlusLang(
"de",
new int[] {
0x20616E, 0x206175, 0x206265, 0x206461, 0x206465, 0x206469, 0x206569, 0x206765, 0x206861, 0x20696E, 0x206D69, 0x207363, 0x207365, 0x20756E, 0x207665, 0x20766F,
0x207765, 0x207A75, 0x626572, 0x636820, 0x636865, 0x636874, 0x646173, 0x64656E, 0x646572, 0x646965, 0x652064, 0x652073, 0x65696E, 0x656974, 0x656E20, 0x657220,
0x657320, 0x67656E, 0x68656E, 0x687420, 0x696368, 0x696520, 0x696E20, 0x696E65, 0x697420, 0x6C6963, 0x6C6C65, 0x6E2061, 0x6E2064, 0x6E2073, 0x6E6420, 0x6E6465,
0x6E6520, 0x6E6720, 0x6E6765, 0x6E7465, 0x722064, 0x726465, 0x726569, 0x736368, 0x737465, 0x742064, 0x746520, 0x74656E, 0x746572, 0x756E64, 0x756E67, 0x766572,
}),
new NGramsPlusLang(
"en",
new int[] {
0x206120, 0x20616E, 0x206265, 0x20636F, 0x20666F, 0x206861, 0x206865, 0x20696E, 0x206D61, 0x206F66, 0x207072, 0x207265, 0x207361, 0x207374, 0x207468, 0x20746F,
0x207768, 0x616964, 0x616C20, 0x616E20, 0x616E64, 0x617320, 0x617420, 0x617465, 0x617469, 0x642061, 0x642074, 0x652061, 0x652073, 0x652074, 0x656420, 0x656E74,
0x657220, 0x657320, 0x666F72, 0x686174, 0x686520, 0x686572, 0x696420, 0x696E20, 0x696E67, 0x696F6E, 0x697320, 0x6E2061, 0x6E2074, 0x6E6420, 0x6E6720, 0x6E7420,
0x6F6620, 0x6F6E20, 0x6F7220, 0x726520, 0x727320, 0x732061, 0x732074, 0x736169, 0x737420, 0x742074, 0x746572, 0x746861, 0x746865, 0x74696F, 0x746F20, 0x747320,
}),
new NGramsPlusLang(
"es",
new int[] {
0x206120, 0x206361, 0x20636F, 0x206465, 0x20656C, 0x20656E, 0x206573, 0x20696E, 0x206C61, 0x206C6F, 0x207061, 0x20706F, 0x207072, 0x207175, 0x207265, 0x207365,
0x20756E, 0x207920, 0x612063, 0x612064, 0x612065, 0x61206C, 0x612070, 0x616369, 0x61646F, 0x616C20, 0x617220, 0x617320, 0x6369F3, 0x636F6E, 0x646520, 0x64656C,
0x646F20, 0x652064, 0x652065, 0x65206C, 0x656C20, 0x656E20, 0x656E74, 0x657320, 0x657374, 0x69656E, 0x69F36E, 0x6C6120, 0x6C6F73, 0x6E2065, 0x6E7465, 0x6F2064,
0x6F2065, 0x6F6E20, 0x6F7220, 0x6F7320, 0x706172, 0x717565, 0x726120, 0x726573, 0x732064, 0x732065, 0x732070, 0x736520, 0x746520, 0x746F20, 0x756520, 0xF36E20,
}),
new NGramsPlusLang(
"fr",
new int[] {
0x206175, 0x20636F, 0x206461, 0x206465, 0x206475, 0x20656E, 0x206574, 0x206C61, 0x206C65, 0x207061, 0x20706F, 0x207072, 0x207175, 0x207365, 0x20736F, 0x20756E,
0x20E020, 0x616E74, 0x617469, 0x636520, 0x636F6E, 0x646520, 0x646573, 0x647520, 0x652061, 0x652063, 0x652064, 0x652065, 0x65206C, 0x652070, 0x652073, 0x656E20,
0x656E74, 0x657220, 0x657320, 0x657420, 0x657572, 0x696F6E, 0x697320, 0x697420, 0x6C6120, 0x6C6520, 0x6C6573, 0x6D656E, 0x6E2064, 0x6E6520, 0x6E7320, 0x6E7420,
0x6F6E20, 0x6F6E74, 0x6F7572, 0x717565, 0x72206C, 0x726520, 0x732061, 0x732064, 0x732065, 0x73206C, 0x732070, 0x742064, 0x746520, 0x74696F, 0x756520, 0x757220,
}),
new NGramsPlusLang(
"it",
new int[] {
0x20616C, 0x206368, 0x20636F, 0x206465, 0x206469, 0x206520, 0x20696C, 0x20696E, 0x206C61, 0x207065, 0x207072, 0x20756E, 0x612063, 0x612064, 0x612070, 0x612073,
0x61746F, 0x636865, 0x636F6E, 0x64656C, 0x646920, 0x652061, 0x652063, 0x652064, 0x652069, 0x65206C, 0x652070, 0x652073, 0x656C20, 0x656C6C, 0x656E74, 0x657220,
0x686520, 0x692061, 0x692063, 0x692064, 0x692073, 0x696120, 0x696C20, 0x696E20, 0x696F6E, 0x6C6120, 0x6C6520, 0x6C6920, 0x6C6C61, 0x6E6520, 0x6E6920, 0x6E6F20,
0x6E7465, 0x6F2061, 0x6F2064, 0x6F2069, 0x6F2073, 0x6F6E20, 0x6F6E65, 0x706572, 0x726120, 0x726520, 0x736920, 0x746120, 0x746520, 0x746920, 0x746F20, 0x7A696F,
}),
new NGramsPlusLang(
"nl",
new int[] {
0x20616C, 0x206265, 0x206461, 0x206465, 0x206469, 0x206565, 0x20656E, 0x206765, 0x206865, 0x20696E, 0x206D61, 0x206D65, 0x206F70, 0x207465, 0x207661, 0x207665,
0x20766F, 0x207765, 0x207A69, 0x61616E, 0x616172, 0x616E20, 0x616E64, 0x617220, 0x617420, 0x636874, 0x646520, 0x64656E, 0x646572, 0x652062, 0x652076, 0x65656E,
0x656572, 0x656E20, 0x657220, 0x657273, 0x657420, 0x67656E, 0x686574, 0x696520, 0x696E20, 0x696E67, 0x697320, 0x6E2062, 0x6E2064, 0x6E2065, 0x6E2068, 0x6E206F,
0x6E2076, 0x6E6465, 0x6E6720, 0x6F6E64, 0x6F6F72, 0x6F7020, 0x6F7220, 0x736368, 0x737465, 0x742064, 0x746520, 0x74656E, 0x746572, 0x76616E, 0x766572, 0x766F6F,
}),
new NGramsPlusLang(
"no",
new int[] {
0x206174, 0x206176, 0x206465, 0x20656E, 0x206572, 0x20666F, 0x206861, 0x206920, 0x206D65, 0x206F67, 0x2070E5, 0x207365, 0x20736B, 0x20736F, 0x207374, 0x207469,
0x207669, 0x20E520, 0x616E64, 0x617220, 0x617420, 0x646520, 0x64656E, 0x646574, 0x652073, 0x656420, 0x656E20, 0x656E65, 0x657220, 0x657265, 0x657420, 0x657474,
0x666F72, 0x67656E, 0x696B6B, 0x696C20, 0x696E67, 0x6B6520, 0x6B6B65, 0x6C6520, 0x6C6C65, 0x6D6564, 0x6D656E, 0x6E2073, 0x6E6520, 0x6E6720, 0x6E6765, 0x6E6E65,
0x6F6720, 0x6F6D20, 0x6F7220, 0x70E520, 0x722073, 0x726520, 0x736F6D, 0x737465, 0x742073, 0x746520, 0x74656E, 0x746572, 0x74696C, 0x747420, 0x747465, 0x766572,
}),
new NGramsPlusLang(
"pt",
new int[] {
0x206120, 0x20636F, 0x206461, 0x206465, 0x20646F, 0x206520, 0x206573, 0x206D61, 0x206E6F, 0x206F20, 0x207061, 0x20706F, 0x207072, 0x207175, 0x207265, 0x207365,
0x20756D, 0x612061, 0x612063, 0x612064, 0x612070, 0x616465, 0x61646F, 0x616C20, 0x617220, 0x617261, 0x617320, 0x636F6D, 0x636F6E, 0x646120, 0x646520, 0x646F20,
0x646F73, 0x652061, 0x652064, 0x656D20, 0x656E74, 0x657320, 0x657374, 0x696120, 0x696361, 0x6D656E, 0x6E7465, 0x6E746F, 0x6F2061, 0x6F2063, 0x6F2064, 0x6F2065,
0x6F2070, 0x6F7320, 0x706172, 0x717565, 0x726120, 0x726573, 0x732061, 0x732064, 0x732065, 0x732070, 0x737461, 0x746520, 0x746F20, 0x756520, 0xE36F20, 0xE7E36F,
}),
new NGramsPlusLang(
"sv",
new int[] {
0x206174, 0x206176, 0x206465, 0x20656E, 0x2066F6, 0x206861, 0x206920, 0x20696E, 0x206B6F, 0x206D65, 0x206F63, 0x2070E5, 0x20736B, 0x20736F, 0x207374, 0x207469,
0x207661, 0x207669, 0x20E472, 0x616465, 0x616E20, 0x616E64, 0x617220, 0x617474, 0x636820, 0x646520, 0x64656E, 0x646572, 0x646574, 0x656420, 0x656E20, 0x657220,
0x657420, 0x66F672, 0x67656E, 0x696C6C, 0x696E67, 0x6B6120, 0x6C6C20, 0x6D6564, 0x6E2073, 0x6E6120, 0x6E6465, 0x6E6720, 0x6E6765, 0x6E696E, 0x6F6368, 0x6F6D20,
0x6F6E20, 0x70E520, 0x722061, 0x722073, 0x726120, 0x736B61, 0x736F6D, 0x742073, 0x746120, 0x746520, 0x746572, 0x74696C, 0x747420, 0x766172, 0xE47220, 0xF67220,
}),
};
@Override
public CharsetMatch match(CharsetDetector det)
{
String name = det.fC1Bytes ? "windows-1252" : "ISO-8859-1";
int bestConfidenceSoFar = -1;
String lang = null;
for (NGramsPlusLang ngl: ngrams_8859_1) {
int confidence = match(det, ngl.fNGrams, byteMap);
if (confidence > bestConfidenceSoFar) {
bestConfidenceSoFar = confidence;
lang = ngl.fLang;
}
}
return bestConfidenceSoFar <= 0 ? null : new CharsetMatch(det, this, bestConfidenceSoFar, name, lang);
}
@Override
public String getName()
{
return "ISO-8859-1";
}
}
static class CharsetRecog_8859_2 extends CharsetRecog_sbcs
{
protected static byte[] byteMap = {
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x00,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0xB1, (byte) 0x20, (byte) 0xB3, (byte) 0x20, (byte) 0xB5, (byte) 0xB6, (byte) 0x20,
(byte) 0x20, (byte) 0xB9, (byte) 0xBA, (byte) 0xBB, (byte) 0xBC, (byte) 0x20, (byte) 0xBE, (byte) 0xBF,
(byte) 0x20, (byte) 0xB1, (byte) 0x20, (byte) 0xB3, (byte) 0x20, (byte) 0xB5, (byte) 0xB6, (byte) 0xB7,
(byte) 0x20, (byte) 0xB9, (byte) 0xBA, (byte) 0xBB, (byte) 0xBC, (byte) 0x20, (byte) 0xBE, (byte) 0xBF,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0xF0, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0x20,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xFC, (byte) 0xFD, (byte) 0xFE, (byte) 0xDF,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0xF0, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0x20,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xFC, (byte) 0xFD, (byte) 0xFE, (byte) 0x20,
};
private static NGramsPlusLang[] ngrams_8859_2 = new NGramsPlusLang[] {
new NGramsPlusLang(
"cs",
new int[] {
0x206120, 0x206279, 0x20646F, 0x206A65, 0x206E61, 0x206E65, 0x206F20, 0x206F64, 0x20706F, 0x207072, 0x2070F8, 0x20726F, 0x207365, 0x20736F, 0x207374, 0x20746F,
0x207620, 0x207679, 0x207A61, 0x612070, 0x636520, 0x636820, 0x652070, 0x652073, 0x652076, 0x656D20, 0x656EED, 0x686F20, 0x686F64, 0x697374, 0x6A6520, 0x6B7465,
0x6C6520, 0x6C6920, 0x6E6120, 0x6EE920, 0x6EEC20, 0x6EED20, 0x6F2070, 0x6F646E, 0x6F6A69, 0x6F7374, 0x6F7520, 0x6F7661, 0x706F64, 0x706F6A, 0x70726F, 0x70F865,
0x736520, 0x736F75, 0x737461, 0x737469, 0x73746E, 0x746572, 0x746EED, 0x746F20, 0x752070, 0xBE6520, 0xE16EED, 0xE9686F, 0xED2070, 0xED2073, 0xED6D20, 0xF86564,
}),
new NGramsPlusLang(
"hu",
new int[] {
0x206120, 0x20617A, 0x206265, 0x206567, 0x20656C, 0x206665, 0x206861, 0x20686F, 0x206973, 0x206B65, 0x206B69, 0x206BF6, 0x206C65, 0x206D61, 0x206D65, 0x206D69,
0x206E65, 0x20737A, 0x207465, 0x20E973, 0x612061, 0x61206B, 0x61206D, 0x612073, 0x616B20, 0x616E20, 0x617A20, 0x62616E, 0x62656E, 0x656779, 0x656B20, 0x656C20,
0x656C65, 0x656D20, 0x656E20, 0x657265, 0x657420, 0x657465, 0x657474, 0x677920, 0x686F67, 0x696E74, 0x697320, 0x6B2061, 0x6BF67A, 0x6D6567, 0x6D696E, 0x6E2061,
0x6E616B, 0x6E656B, 0x6E656D, 0x6E7420, 0x6F6779, 0x732061, 0x737A65, 0x737A74, 0x737AE1, 0x73E967, 0x742061, 0x747420, 0x74E173, 0x7A6572, 0xE16E20, 0xE97320,
}),
new NGramsPlusLang(
"pl",
new int[] {
0x20637A, 0x20646F, 0x206920, 0x206A65, 0x206B6F, 0x206D61, 0x206D69, 0x206E61, 0x206E69, 0x206F64, 0x20706F, 0x207072, 0x207369, 0x207720, 0x207769, 0x207779,
0x207A20, 0x207A61, 0x612070, 0x612077, 0x616E69, 0x636820, 0x637A65, 0x637A79, 0x646F20, 0x647A69, 0x652070, 0x652073, 0x652077, 0x65207A, 0x65676F, 0x656A20,
0x656D20, 0x656E69, 0x676F20, 0x696120, 0x696520, 0x69656A, 0x6B6120, 0x6B6920, 0x6B6965, 0x6D6965, 0x6E6120, 0x6E6961, 0x6E6965, 0x6F2070, 0x6F7761, 0x6F7769,
0x706F6C, 0x707261, 0x70726F, 0x70727A, 0x727A65, 0x727A79, 0x7369EA, 0x736B69, 0x737461, 0x776965, 0x796368, 0x796D20, 0x7A6520, 0x7A6965, 0x7A7920, 0xF37720,
}),
new NGramsPlusLang(
"ro",
new int[] {
0x206120, 0x206163, 0x206361, 0x206365, 0x20636F, 0x206375, 0x206465, 0x206469, 0x206C61, 0x206D61, 0x207065, 0x207072, 0x207365, 0x2073E3, 0x20756E, 0x20BA69,
0x20EE6E, 0x612063, 0x612064, 0x617265, 0x617420, 0x617465, 0x617520, 0x636172, 0x636F6E, 0x637520, 0x63E320, 0x646520, 0x652061, 0x652063, 0x652064, 0x652070,
0x652073, 0x656120, 0x656920, 0x656C65, 0x656E74, 0x657374, 0x692061, 0x692063, 0x692064, 0x692070, 0x696520, 0x696920, 0x696E20, 0x6C6120, 0x6C6520, 0x6C6F72,
0x6C7569, 0x6E6520, 0x6E7472, 0x6F7220, 0x70656E, 0x726520, 0x726561, 0x727520, 0x73E320, 0x746520, 0x747275, 0x74E320, 0x756920, 0x756C20, 0xBA6920, 0xEE6E20,
})
};
@Override
public CharsetMatch match(CharsetDetector det)
{
String name = det.fC1Bytes ? "windows-1250" : "ISO-8859-2";
int bestConfidenceSoFar = -1;
String lang = null;
for (NGramsPlusLang ngl: ngrams_8859_2) {
int confidence = match(det, ngl.fNGrams, byteMap);
if (confidence > bestConfidenceSoFar) {
bestConfidenceSoFar = confidence;
lang = ngl.fLang;
}
}
return bestConfidenceSoFar <= 0 ? null : new CharsetMatch(det, this, bestConfidenceSoFar, name, lang);
}
@Override
public String getName()
{
return "ISO-8859-2";
}
}
abstract static class CharsetRecog_8859_5 extends CharsetRecog_sbcs
{
protected static byte[] byteMap = {
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x00,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xFC, (byte) 0x20, (byte) 0xFE, (byte) 0xFF,
(byte) 0xD0, (byte) 0xD1, (byte) 0xD2, (byte) 0xD3, (byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0xD7,
(byte) 0xD8, (byte) 0xD9, (byte) 0xDA, (byte) 0xDB, (byte) 0xDC, (byte) 0xDD, (byte) 0xDE, (byte) 0xDF,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0xD0, (byte) 0xD1, (byte) 0xD2, (byte) 0xD3, (byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0xD7,
(byte) 0xD8, (byte) 0xD9, (byte) 0xDA, (byte) 0xDB, (byte) 0xDC, (byte) 0xDD, (byte) 0xDE, (byte) 0xDF,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0x20, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xFC, (byte) 0x20, (byte) 0xFE, (byte) 0xFF,
};
@Override
public String getName()
{
return "ISO-8859-5";
}
}
static class CharsetRecog_8859_5_ru extends CharsetRecog_8859_5
{
private static int[] ngrams = {
0x20D220, 0x20D2DE, 0x20D4DE, 0x20D7D0, 0x20D820, 0x20DAD0, 0x20DADE, 0x20DDD0, 0x20DDD5, 0x20DED1, 0x20DFDE, 0x20DFE0, 0x20E0D0, 0x20E1DE, 0x20E1E2, 0x20E2DE,
0x20E7E2, 0x20EDE2, 0xD0DDD8, 0xD0E2EC, 0xD3DE20, 0xD5DBEC, 0xD5DDD8, 0xD5E1E2, 0xD5E220, 0xD820DF, 0xD8D520, 0xD8D820, 0xD8EF20, 0xDBD5DD, 0xDBD820, 0xDBECDD,
0xDDD020, 0xDDD520, 0xDDD8D5, 0xDDD8EF, 0xDDDE20, 0xDDDED2, 0xDE20D2, 0xDE20DF, 0xDE20E1, 0xDED220, 0xDED2D0, 0xDED3DE, 0xDED920, 0xDEDBEC, 0xDEDC20, 0xDEE1E2,
0xDFDEDB, 0xDFE0D5, 0xDFE0D8, 0xDFE0DE, 0xE0D0D2, 0xE0D5D4, 0xE1E2D0, 0xE1E2D2, 0xE1E2D8, 0xE1EF20, 0xE2D5DB, 0xE2DE20, 0xE2DEE0, 0xE2EC20, 0xE7E2DE, 0xEBE520,
};
@Override
public String getLanguage()
{
return "ru";
}
@Override
public CharsetMatch match(CharsetDetector det)
{
int confidence = match(det, ngrams, byteMap);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
}
abstract static class CharsetRecog_8859_6 extends CharsetRecog_sbcs
{
protected static byte[] byteMap = {
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x00,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0xC1, (byte) 0xC2, (byte) 0xC3, (byte) 0xC4, (byte) 0xC5, (byte) 0xC6, (byte) 0xC7,
(byte) 0xC8, (byte) 0xC9, (byte) 0xCA, (byte) 0xCB, (byte) 0xCC, (byte) 0xCD, (byte) 0xCE, (byte) 0xCF,
(byte) 0xD0, (byte) 0xD1, (byte) 0xD2, (byte) 0xD3, (byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0xD7,
(byte) 0xD8, (byte) 0xD9, (byte) 0xDA, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
};
@Override
public String getName()
{
return "ISO-8859-6";
}
}
static class CharsetRecog_8859_6_ar extends CharsetRecog_8859_6
{
private static int[] ngrams = {
0x20C7E4, 0x20C7E6, 0x20C8C7, 0x20D9E4, 0x20E1EA, 0x20E4E4, 0x20E5E6, 0x20E8C7, 0xC720C7, 0xC7C120, 0xC7CA20, 0xC7D120, 0xC7E420, 0xC7E4C3, 0xC7E4C7, 0xC7E4C8,
0xC7E4CA, 0xC7E4CC, 0xC7E4CD, 0xC7E4CF, 0xC7E4D3, 0xC7E4D9, 0xC7E4E2, 0xC7E4E5, 0xC7E4E8, 0xC7E4EA, 0xC7E520, 0xC7E620, 0xC7E6CA, 0xC820C7, 0xC920C7, 0xC920E1,
0xC920E4, 0xC920E5, 0xC920E8, 0xCA20C7, 0xCF20C7, 0xCFC920, 0xD120C7, 0xD1C920, 0xD320C7, 0xD920C7, 0xD9E4E9, 0xE1EA20, 0xE420C7, 0xE4C920, 0xE4E920, 0xE4EA20,
0xE520C7, 0xE5C720, 0xE5C920, 0xE5E620, 0xE620C7, 0xE720C7, 0xE7C720, 0xE8C7E4, 0xE8E620, 0xE920C7, 0xEA20C7, 0xEA20E5, 0xEA20E8, 0xEAC920, 0xEAD120, 0xEAE620,
};
@Override
public String getLanguage()
{
return "ar";
}
@Override
public CharsetMatch match(CharsetDetector det)
{
int confidence = match(det, ngrams, byteMap);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
}
abstract static class CharsetRecog_8859_7 extends CharsetRecog_sbcs
{
protected static byte[] byteMap = {
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x00,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0xA1, (byte) 0xA2, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0xDC, (byte) 0x20,
(byte) 0xDD, (byte) 0xDE, (byte) 0xDF, (byte) 0x20, (byte) 0xFC, (byte) 0x20, (byte) 0xFD, (byte) 0xFE,
(byte) 0xC0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0xF0, (byte) 0xF1, (byte) 0x20, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xDC, (byte) 0xDD, (byte) 0xDE, (byte) 0xDF,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0xF0, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xFC, (byte) 0xFD, (byte) 0xFE, (byte) 0x20,
};
@Override
public String getName()
{
return "ISO-8859-7";
}
}
static class CharsetRecog_8859_7_el extends CharsetRecog_8859_7
{
private static int[] ngrams = {
0x20E1ED, 0x20E1F0, 0x20E3E9, 0x20E4E9, 0x20E5F0, 0x20E720, 0x20EAE1, 0x20ECE5, 0x20EDE1, 0x20EF20, 0x20F0E1, 0x20F0EF, 0x20F0F1, 0x20F3F4, 0x20F3F5, 0x20F4E7,
0x20F4EF, 0xDFE120, 0xE120E1, 0xE120F4, 0xE1E920, 0xE1ED20, 0xE1F0FC, 0xE1F220, 0xE3E9E1, 0xE5E920, 0xE5F220, 0xE720F4, 0xE7ED20, 0xE7F220, 0xE920F4, 0xE9E120,
0xE9EADE, 0xE9F220, 0xEAE1E9, 0xEAE1F4, 0xECE520, 0xED20E1, 0xED20E5, 0xED20F0, 0xEDE120, 0xEFF220, 0xEFF520, 0xF0EFF5, 0xF0F1EF, 0xF0FC20, 0xF220E1, 0xF220E5,
0xF220EA, 0xF220F0, 0xF220F4, 0xF3E520, 0xF3E720, 0xF3F4EF, 0xF4E120, 0xF4E1E9, 0xF4E7ED, 0xF4E7F2, 0xF4E9EA, 0xF4EF20, 0xF4EFF5, 0xF4F9ED, 0xF9ED20, 0xFEED20,
};
@Override
public String getLanguage()
{
return "el";
}
@Override
public CharsetMatch match(CharsetDetector det)
{
String name = det.fC1Bytes ? "windows-1253" : "ISO-8859-7";
int confidence = match(det, ngrams, byteMap);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence, name, "el");
}
}
abstract static class CharsetRecog_8859_8 extends CharsetRecog_sbcs
{
protected static byte[] byteMap = {
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x00,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0xB5, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0xF0, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
};
@Override
public String getName()
{
return "ISO-8859-8";
}
}
static class CharsetRecog_8859_8_I_he extends CharsetRecog_8859_8
{
private static int[] ngrams = {
0x20E0E5, 0x20E0E7, 0x20E0E9, 0x20E0FA, 0x20E1E9, 0x20E1EE, 0x20E4E0, 0x20E4E5, 0x20E4E9, 0x20E4EE, 0x20E4F2, 0x20E4F9, 0x20E4FA, 0x20ECE0, 0x20ECE4, 0x20EEE0,
0x20F2EC, 0x20F9EC, 0xE0FA20, 0xE420E0, 0xE420E1, 0xE420E4, 0xE420EC, 0xE420EE, 0xE420F9, 0xE4E5E0, 0xE5E020, 0xE5ED20, 0xE5EF20, 0xE5F820, 0xE5FA20, 0xE920E4,
0xE9E420, 0xE9E5FA, 0xE9E9ED, 0xE9ED20, 0xE9EF20, 0xE9F820, 0xE9FA20, 0xEC20E0, 0xEC20E4, 0xECE020, 0xECE420, 0xED20E0, 0xED20E1, 0xED20E4, 0xED20EC, 0xED20EE,
0xED20F9, 0xEEE420, 0xEF20E4, 0xF0E420, 0xF0E920, 0xF0E9ED, 0xF2EC20, 0xF820E4, 0xF8E9ED, 0xF9EC20, 0xFA20E0, 0xFA20E1, 0xFA20E4, 0xFA20EC, 0xFA20EE, 0xFA20F9,
};
@Override
public String getName()
{
return "ISO-8859-8-I";
}
@Override
public String getLanguage()
{
return "he";
}
@Override
public CharsetMatch match(CharsetDetector det)
{
String name = det.fC1Bytes ? "windows-1255" : "ISO-8859-8-I";
int confidence = match(det, ngrams, byteMap);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence, name, "he");
}
}
static class CharsetRecog_8859_8_he extends CharsetRecog_8859_8
{
private static int[] ngrams = {
0x20E0E5, 0x20E0EC, 0x20E4E9, 0x20E4EC, 0x20E4EE, 0x20E4F0, 0x20E9F0, 0x20ECF2, 0x20ECF9, 0x20EDE5, 0x20EDE9, 0x20EFE5, 0x20EFE9, 0x20F8E5, 0x20F8E9, 0x20FAE0,
0x20FAE5, 0x20FAE9, 0xE020E4, 0xE020EC, 0xE020ED, 0xE020FA, 0xE0E420, 0xE0E5E4, 0xE0EC20, 0xE0EE20, 0xE120E4, 0xE120ED, 0xE120FA, 0xE420E4, 0xE420E9, 0xE420EC,
0xE420ED, 0xE420EF, 0xE420F8, 0xE420FA, 0xE4EC20, 0xE5E020, 0xE5E420, 0xE7E020, 0xE9E020, 0xE9E120, 0xE9E420, 0xEC20E4, 0xEC20ED, 0xEC20FA, 0xECF220, 0xECF920,
0xEDE9E9, 0xEDE9F0, 0xEDE9F8, 0xEE20E4, 0xEE20ED, 0xEE20FA, 0xEEE120, 0xEEE420, 0xF2E420, 0xF920E4, 0xF920ED, 0xF920FA, 0xF9E420, 0xFAE020, 0xFAE420, 0xFAE5E9,
};
@Override
public String getLanguage()
{
return "he";
}
@Override
public CharsetMatch match(CharsetDetector det)
{
String name = det.fC1Bytes ? "windows-1255" : "ISO-8859-8";
int confidence = match(det, ngrams, byteMap);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence, name, "he");
}
}
abstract static class CharsetRecog_8859_9 extends CharsetRecog_sbcs
{
protected static byte[] byteMap = {
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x00,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0xAA, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0xB5, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0xBA, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0xF0, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0x20,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xFC, (byte) 0x69, (byte) 0xFE, (byte) 0xDF,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0xF0, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0x20,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xFC, (byte) 0xFD, (byte) 0xFE, (byte) 0xFF,
};
@Override
public String getName()
{
return "ISO-8859-9";
}
}
static class CharsetRecog_8859_9_tr extends CharsetRecog_8859_9
{
private static int[] ngrams = {
0x206261, 0x206269, 0x206275, 0x206461, 0x206465, 0x206765, 0x206861, 0x20696C, 0x206B61, 0x206B6F, 0x206D61, 0x206F6C, 0x207361, 0x207461, 0x207665, 0x207961,
0x612062, 0x616B20, 0x616C61, 0x616D61, 0x616E20, 0x616EFD, 0x617220, 0x617261, 0x6172FD, 0x6173FD, 0x617961, 0x626972, 0x646120, 0x646520, 0x646920, 0x652062,
0x65206B, 0x656469, 0x656E20, 0x657220, 0x657269, 0x657369, 0x696C65, 0x696E20, 0x696E69, 0x697220, 0x6C616E, 0x6C6172, 0x6C6520, 0x6C6572, 0x6E2061, 0x6E2062,
0x6E206B, 0x6E6461, 0x6E6465, 0x6E6520, 0x6E6920, 0x6E696E, 0x6EFD20, 0x72696E, 0x72FD6E, 0x766520, 0x796120, 0x796F72, 0xFD6E20, 0xFD6E64, 0xFD6EFD, 0xFDF0FD,
};
@Override
public String getLanguage()
{
return "tr";
}
@Override
public CharsetMatch match(CharsetDetector det)
{
String name = det.fC1Bytes ? "windows-1254" : "ISO-8859-9";
int confidence = match(det, ngrams, byteMap);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence, name, "tr");
}
}
static class CharsetRecog_windows_1251 extends CharsetRecog_sbcs
{
private static int[] ngrams = {
0x20E220, 0x20E2EE, 0x20E4EE, 0x20E7E0, 0x20E820, 0x20EAE0, 0x20EAEE, 0x20EDE0, 0x20EDE5, 0x20EEE1, 0x20EFEE, 0x20EFF0, 0x20F0E0, 0x20F1EE, 0x20F1F2, 0x20F2EE,
0x20F7F2, 0x20FDF2, 0xE0EDE8, 0xE0F2FC, 0xE3EE20, 0xE5EBFC, 0xE5EDE8, 0xE5F1F2, 0xE5F220, 0xE820EF, 0xE8E520, 0xE8E820, 0xE8FF20, 0xEBE5ED, 0xEBE820, 0xEBFCED,
0xEDE020, 0xEDE520, 0xEDE8E5, 0xEDE8FF, 0xEDEE20, 0xEDEEE2, 0xEE20E2, 0xEE20EF, 0xEE20F1, 0xEEE220, 0xEEE2E0, 0xEEE3EE, 0xEEE920, 0xEEEBFC, 0xEEEC20, 0xEEF1F2,
0xEFEEEB, 0xEFF0E5, 0xEFF0E8, 0xEFF0EE, 0xF0E0E2, 0xF0E5E4, 0xF1F2E0, 0xF1F2E2, 0xF1F2E8, 0xF1FF20, 0xF2E5EB, 0xF2EE20, 0xF2EEF0, 0xF2FC20, 0xF7F2EE, 0xFBF520,
};
private static byte[] byteMap = {
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x00,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x90, (byte) 0x83, (byte) 0x20, (byte) 0x83, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x9A, (byte) 0x20, (byte) 0x9C, (byte) 0x9D, (byte) 0x9E, (byte) 0x9F,
(byte) 0x90, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x9A, (byte) 0x20, (byte) 0x9C, (byte) 0x9D, (byte) 0x9E, (byte) 0x9F,
(byte) 0x20, (byte) 0xA2, (byte) 0xA2, (byte) 0xBC, (byte) 0x20, (byte) 0xB4, (byte) 0x20, (byte) 0x20,
(byte) 0xB8, (byte) 0x20, (byte) 0xBA, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0xBF,
(byte) 0x20, (byte) 0x20, (byte) 0xB3, (byte) 0xB3, (byte) 0xB4, (byte) 0xB5, (byte) 0x20, (byte) 0x20,
(byte) 0xB8, (byte) 0x20, (byte) 0xBA, (byte) 0x20, (byte) 0xBC, (byte) 0xBE, (byte) 0xBE, (byte) 0xBF,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0xF0, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xFC, (byte) 0xFD, (byte) 0xFE, (byte) 0xFF,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0xF0, (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5, (byte) 0xF6, (byte) 0xF7,
(byte) 0xF8, (byte) 0xF9, (byte) 0xFA, (byte) 0xFB, (byte) 0xFC, (byte) 0xFD, (byte) 0xFE, (byte) 0xFF,
};
@Override
public String getName()
{
return "windows-1251";
}
@Override
public String getLanguage()
{
return "ru";
}
@Override
public CharsetMatch match(CharsetDetector det)
{
int confidence = match(det, ngrams, byteMap);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
}
static class CharsetRecog_windows_1256 extends CharsetRecog_sbcs
{
private static int[] ngrams = {
0x20C7E1, 0x20C7E4, 0x20C8C7, 0x20DAE1, 0x20DDED, 0x20E1E1, 0x20E3E4, 0x20E6C7, 0xC720C7, 0xC7C120, 0xC7CA20, 0xC7D120, 0xC7E120, 0xC7E1C3, 0xC7E1C7, 0xC7E1C8,
0xC7E1CA, 0xC7E1CC, 0xC7E1CD, 0xC7E1CF, 0xC7E1D3, 0xC7E1DA, 0xC7E1DE, 0xC7E1E3, 0xC7E1E6, 0xC7E1ED, 0xC7E320, 0xC7E420, 0xC7E4CA, 0xC820C7, 0xC920C7, 0xC920DD,
0xC920E1, 0xC920E3, 0xC920E6, 0xCA20C7, 0xCF20C7, 0xCFC920, 0xD120C7, 0xD1C920, 0xD320C7, 0xDA20C7, 0xDAE1EC, 0xDDED20, 0xE120C7, 0xE1C920, 0xE1EC20, 0xE1ED20,
0xE320C7, 0xE3C720, 0xE3C920, 0xE3E420, 0xE420C7, 0xE520C7, 0xE5C720, 0xE6C7E1, 0xE6E420, 0xEC20C7, 0xED20C7, 0xED20E3, 0xED20E6, 0xEDC920, 0xEDD120, 0xEDE420,
};
private static byte[] byteMap = {
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x00,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x81, (byte) 0x20, (byte) 0x83, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x88, (byte) 0x20, (byte) 0x8A, (byte) 0x20, (byte) 0x9C, (byte) 0x8D, (byte) 0x8E, (byte) 0x8F,
(byte) 0x90, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x98, (byte) 0x20, (byte) 0x9A, (byte) 0x20, (byte) 0x9C, (byte) 0x20, (byte) 0x20, (byte) 0x9F,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0xAA, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0xB5, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0xC0, (byte) 0xC1, (byte) 0xC2, (byte) 0xC3, (byte) 0xC4, (byte) 0xC5, (byte) 0xC6, (byte) 0xC7,
(byte) 0xC8, (byte) 0xC9, (byte) 0xCA, (byte) 0xCB, (byte) 0xCC, (byte) 0xCD, (byte) 0xCE, (byte) 0xCF,
(byte) 0xD0, (byte) 0xD1, (byte) 0xD2, (byte) 0xD3, (byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0x20,
(byte) 0xD8, (byte) 0xD9, (byte) 0xDA, (byte) 0xDB, (byte) 0xDC, (byte) 0xDD, (byte) 0xDE, (byte) 0xDF,
(byte) 0xE0, (byte) 0xE1, (byte) 0xE2, (byte) 0xE3, (byte) 0xE4, (byte) 0xE5, (byte) 0xE6, (byte) 0xE7,
(byte) 0xE8, (byte) 0xE9, (byte) 0xEA, (byte) 0xEB, (byte) 0xEC, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0xF4, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0xF9, (byte) 0x20, (byte) 0xFB, (byte) 0xFC, (byte) 0x20, (byte) 0x20, (byte) 0xFF,
};
@Override
public String getName()
{
return "windows-1256";
}
@Override
public String getLanguage()
{
return "ar";
}
@Override
public CharsetMatch match(CharsetDetector det)
{
int confidence = match(det, ngrams, byteMap);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
}
static class CharsetRecog_KOI8_R extends CharsetRecog_sbcs
{
private static int[] ngrams = {
0x20C4CF, 0x20C920, 0x20CBC1, 0x20CBCF, 0x20CEC1, 0x20CEC5, 0x20CFC2, 0x20D0CF, 0x20D0D2, 0x20D2C1, 0x20D3CF, 0x20D3D4, 0x20D4CF, 0x20D720, 0x20D7CF, 0x20DAC1,
0x20DCD4, 0x20DED4, 0xC1CEC9, 0xC1D4D8, 0xC5CCD8, 0xC5CEC9, 0xC5D3D4, 0xC5D420, 0xC7CF20, 0xC920D0, 0xC9C520, 0xC9C920, 0xC9D120, 0xCCC5CE, 0xCCC920, 0xCCD8CE,
0xCEC120, 0xCEC520, 0xCEC9C5, 0xCEC9D1, 0xCECF20, 0xCECFD7, 0xCF20D0, 0xCF20D3, 0xCF20D7, 0xCFC7CF, 0xCFCA20, 0xCFCCD8, 0xCFCD20, 0xCFD3D4, 0xCFD720, 0xCFD7C1,
0xD0CFCC, 0xD0D2C5, 0xD0D2C9, 0xD0D2CF, 0xD2C1D7, 0xD2C5C4, 0xD3D120, 0xD3D4C1, 0xD3D4C9, 0xD3D4D7, 0xD4C5CC, 0xD4CF20, 0xD4CFD2, 0xD4D820, 0xD9C820, 0xDED4CF,
};
private static byte[] byteMap = {
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x00,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67,
(byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B, (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F,
(byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77,
(byte) 0x78, (byte) 0x79, (byte) 0x7A, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0xA3, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0xA3, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20, (byte) 0x20,
(byte) 0xC0, (byte) 0xC1, (byte) 0xC2, (byte) 0xC3, (byte) 0xC4, (byte) 0xC5, (byte) 0xC6, (byte) 0xC7,
(byte) 0xC8, (byte) 0xC9, (byte) 0xCA, (byte) 0xCB, (byte) 0xCC, (byte) 0xCD, (byte) 0xCE, (byte) 0xCF,
(byte) 0xD0, (byte) 0xD1, (byte) 0xD2, (byte) 0xD3, (byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0xD7,
(byte) 0xD8, (byte) 0xD9, (byte) 0xDA, (byte) 0xDB, (byte) 0xDC, (byte) 0xDD, (byte) 0xDE, (byte) 0xDF,
(byte) 0xC0, (byte) 0xC1, (byte) 0xC2, (byte) 0xC3, (byte) 0xC4, (byte) 0xC5, (byte) 0xC6, (byte) 0xC7,
(byte) 0xC8, (byte) 0xC9, (byte) 0xCA, (byte) 0xCB, (byte) 0xCC, (byte) 0xCD, (byte) 0xCE, (byte) 0xCF,
(byte) 0xD0, (byte) 0xD1, (byte) 0xD2, (byte) 0xD3, (byte) 0xD4, (byte) 0xD5, (byte) 0xD6, (byte) 0xD7,
(byte) 0xD8, (byte) 0xD9, (byte) 0xDA, (byte) 0xDB, (byte) 0xDC, (byte) 0xDD, (byte) 0xDE, (byte) 0xDF,
};
@Override
public String getName()
{
return "KOI8-R";
}
@Override
public String getLanguage()
{
return "ru";
}
@Override
public CharsetMatch match(CharsetDetector det)
{
int confidence = match(det, ngrams, byteMap);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
}
abstract static class CharsetRecog_IBM424_he extends CharsetRecog_sbcs
{
protected static byte[] byteMap = {
/* -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F */
/* 0- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 1- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 2- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 3- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 4- */ (byte) 0x40, (byte) 0x41, (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47, (byte) 0x48, (byte) 0x49, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 5- */ (byte) 0x40, (byte) 0x51, (byte) 0x52, (byte) 0x53, (byte) 0x54, (byte) 0x55, (byte) 0x56, (byte) 0x57, (byte) 0x58, (byte) 0x59, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 6- */ (byte) 0x40, (byte) 0x40, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 7- */ (byte) 0x40, (byte) 0x71, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x00, (byte) 0x40, (byte) 0x40,
/* 8- */ (byte) 0x40, (byte) 0x81, (byte) 0x82, (byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 9- */ (byte) 0x40, (byte) 0x91, (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98, (byte) 0x99, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* A- */ (byte) 0xA0, (byte) 0x40, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5, (byte) 0xA6, (byte) 0xA7, (byte) 0xA8, (byte) 0xA9, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* B- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* C- */ (byte) 0x40, (byte) 0x81, (byte) 0x82, (byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* D- */ (byte) 0x40, (byte) 0x91, (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98, (byte) 0x99, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* E- */ (byte) 0x40, (byte) 0x40, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5, (byte) 0xA6, (byte) 0xA7, (byte) 0xA8, (byte) 0xA9, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* F- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
};
@Override
public String getLanguage()
{
return "he";
}
}
static class CharsetRecog_IBM424_he_rtl extends CharsetRecog_IBM424_he
{
@Override
public String getName()
{
return "IBM424_rtl";
}
private static int[] ngrams = {
0x404146, 0x404148, 0x404151, 0x404171, 0x404251, 0x404256, 0x404541, 0x404546, 0x404551, 0x404556, 0x404562, 0x404569, 0x404571, 0x405441, 0x405445, 0x405641,
0x406254, 0x406954, 0x417140, 0x454041, 0x454042, 0x454045, 0x454054, 0x454056, 0x454069, 0x454641, 0x464140, 0x465540, 0x465740, 0x466840, 0x467140, 0x514045,
0x514540, 0x514671, 0x515155, 0x515540, 0x515740, 0x516840, 0x517140, 0x544041, 0x544045, 0x544140, 0x544540, 0x554041, 0x554042, 0x554045, 0x554054, 0x554056,
0x554069, 0x564540, 0x574045, 0x584540, 0x585140, 0x585155, 0x625440, 0x684045, 0x685155, 0x695440, 0x714041, 0x714042, 0x714045, 0x714054, 0x714056, 0x714069,
};
@Override
public CharsetMatch match(CharsetDetector det)
{
int confidence = match(det, ngrams, byteMap, (byte)0x40);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
}
static class CharsetRecog_IBM424_he_ltr extends CharsetRecog_IBM424_he
{
@Override
public String getName()
{
return "IBM424_ltr";
}
private static int[] ngrams = {
0x404146, 0x404154, 0x404551, 0x404554, 0x404556, 0x404558, 0x405158, 0x405462, 0x405469, 0x405546, 0x405551, 0x405746, 0x405751, 0x406846, 0x406851, 0x407141,
0x407146, 0x407151, 0x414045, 0x414054, 0x414055, 0x414071, 0x414540, 0x414645, 0x415440, 0x415640, 0x424045, 0x424055, 0x424071, 0x454045, 0x454051, 0x454054,
0x454055, 0x454057, 0x454068, 0x454071, 0x455440, 0x464140, 0x464540, 0x484140, 0x514140, 0x514240, 0x514540, 0x544045, 0x544055, 0x544071, 0x546240, 0x546940,
0x555151, 0x555158, 0x555168, 0x564045, 0x564055, 0x564071, 0x564240, 0x564540, 0x624540, 0x694045, 0x694055, 0x694071, 0x694540, 0x714140, 0x714540, 0x714651
};
@Override
public CharsetMatch match(CharsetDetector det)
{
int confidence = match(det, ngrams, byteMap, (byte)0x40);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
}
abstract static class CharsetRecog_IBM420_ar extends CharsetRecog_sbcs
{
protected static byte[] byteMap = {
/* -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F */
/* 0- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 1- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 2- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 3- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 4- */ (byte) 0x40, (byte) 0x40, (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47, (byte) 0x48, (byte) 0x49, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 5- */ (byte) 0x40, (byte) 0x51, (byte) 0x52, (byte) 0x40, (byte) 0x40, (byte) 0x55, (byte) 0x56, (byte) 0x57, (byte) 0x58, (byte) 0x59, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 6- */ (byte) 0x40, (byte) 0x40, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 7- */ (byte) 0x70, (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75, (byte) 0x76, (byte) 0x77, (byte) 0x78, (byte) 0x79, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40,
/* 8- */ (byte) 0x80, (byte) 0x81, (byte) 0x82, (byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89, (byte) 0x8A, (byte) 0x8B, (byte) 0x8C, (byte) 0x8D, (byte) 0x8E, (byte) 0x8F,
/* 9- */ (byte) 0x90, (byte) 0x91, (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98, (byte) 0x99, (byte) 0x9A, (byte) 0x9B, (byte) 0x9C, (byte) 0x9D, (byte) 0x9E, (byte) 0x9F,
/* A- */ (byte) 0xA0, (byte) 0x40, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5, (byte) 0xA6, (byte) 0xA7, (byte) 0xA8, (byte) 0xA9, (byte) 0xAA, (byte) 0xAB, (byte) 0xAC, (byte) 0xAD, (byte) 0xAE, (byte) 0xAF,
/* B- */ (byte) 0xB0, (byte) 0xB1, (byte) 0xB2, (byte) 0xB3, (byte) 0xB4, (byte) 0xB5, (byte) 0x40, (byte) 0x40, (byte) 0xB8, (byte) 0xB9, (byte) 0xBA, (byte) 0xBB, (byte) 0xBC, (byte) 0xBD, (byte) 0xBE, (byte) 0xBF,
/* C- */ (byte) 0x40, (byte) 0x81, (byte) 0x82, (byte) 0x83, (byte) 0x84, (byte) 0x85, (byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89, (byte) 0x40, (byte) 0xCB, (byte) 0x40, (byte) 0xCD, (byte) 0x40, (byte) 0xCF,
/* D- */ (byte) 0x40, (byte) 0x91, (byte) 0x92, (byte) 0x93, (byte) 0x94, (byte) 0x95, (byte) 0x96, (byte) 0x97, (byte) 0x98, (byte) 0x99, (byte) 0xDA, (byte) 0xDB, (byte) 0xDC, (byte) 0xDD, (byte) 0xDE, (byte) 0xDF,
/* E- */ (byte) 0x40, (byte) 0x40, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5, (byte) 0xA6, (byte) 0xA7, (byte) 0xA8, (byte) 0xA9, (byte) 0xEA, (byte) 0xEB, (byte) 0x40, (byte) 0xED, (byte) 0xEE, (byte) 0xEF,
/* F- */ (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0x40, (byte) 0xFB, (byte) 0xFC, (byte) 0xFD, (byte) 0xFE, (byte) 0x40,
};
@Override
public String getLanguage()
{
return "ar";
}
}
static class CharsetRecog_IBM420_ar_rtl extends CharsetRecog_IBM420_ar
{
private static int[] ngrams = {
0x4056B1, 0x4056BD, 0x405856, 0x409AB1, 0x40ABDC, 0x40B1B1, 0x40BBBD, 0x40CF56, 0x564056, 0x564640, 0x566340, 0x567540, 0x56B140, 0x56B149, 0x56B156, 0x56B158,
0x56B163, 0x56B167, 0x56B169, 0x56B173, 0x56B178, 0x56B19A, 0x56B1AD, 0x56B1BB, 0x56B1CF, 0x56B1DC, 0x56BB40, 0x56BD40, 0x56BD63, 0x584056, 0x624056, 0x6240AB,
0x6240B1, 0x6240BB, 0x6240CF, 0x634056, 0x734056, 0x736240, 0x754056, 0x756240, 0x784056, 0x9A4056, 0x9AB1DA, 0xABDC40, 0xB14056, 0xB16240, 0xB1DA40, 0xB1DC40,
0xBB4056, 0xBB5640, 0xBB6240, 0xBBBD40, 0xBD4056, 0xBF4056, 0xBF5640, 0xCF56B1, 0xCFBD40, 0xDA4056, 0xDC4056, 0xDC40BB, 0xDC40CF, 0xDC6240, 0xDC7540, 0xDCBD40,
};
@Override
public String getName()
{
return "IBM420_rtl";
}
@Override
public CharsetMatch match(CharsetDetector det)
{
int confidence = matchIBM420(det, ngrams, byteMap, (byte)0x40);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
}
static class CharsetRecog_IBM420_ar_ltr extends CharsetRecog_IBM420_ar
{
private static int[] ngrams = {
0x404656, 0x4056BB, 0x4056BF, 0x406273, 0x406275, 0x4062B1, 0x4062BB, 0x4062DC, 0x406356, 0x407556, 0x4075DC, 0x40B156, 0x40BB56, 0x40BD56, 0x40BDBB, 0x40BDCF,
0x40BDDC, 0x40DAB1, 0x40DCAB, 0x40DCB1, 0x49B156, 0x564056, 0x564058, 0x564062, 0x564063, 0x564073, 0x564075, 0x564078, 0x56409A, 0x5640B1, 0x5640BB, 0x5640BD,
0x5640BF, 0x5640DA, 0x5640DC, 0x565840, 0x56B156, 0x56CF40, 0x58B156, 0x63B156, 0x63BD56, 0x67B156, 0x69B156, 0x73B156, 0x78B156, 0x9AB156, 0xAB4062, 0xADB156,
0xB14062, 0xB15640, 0xB156CF, 0xB19A40, 0xB1B140, 0xBB4062, 0xBB40DC, 0xBBB156, 0xBD5640, 0xBDBB40, 0xCF4062, 0xCF40DC, 0xCFB156, 0xDAB19A, 0xDCAB40, 0xDCB156
};
@Override
public String getName()
{
return "IBM420_ltr";
}
@Override
public CharsetMatch match(CharsetDetector det)
{
int confidence = matchIBM420(det, ngrams, byteMap, (byte)0x40);
return confidence == 0 ? null : new CharsetMatch(det, this, confidence);
}
}
}
================================================
FILE: src/main/java/com/ibm/icu/text/CharsetRecognizer.java
================================================
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/**
*******************************************************************************
* Copyright (C) 2005-2012, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.text;
/**
* Abstract class for recognizing a single charset.
* Part of the implementation of ICU's CharsetDetector.
*
* Each specific charset that can be recognized will have an instance
* of some subclass of this class. All interaction between the overall
* CharsetDetector and the stuff specific to an individual charset happens
* via the interface provided here.
*
* Instances of CharsetDetector DO NOT have or maintain
* state pertaining to a specific match or detect operation.
* The WILL be shared by multiple instances of CharsetDetector.
* They encapsulate const charset-specific information.
*/
abstract class CharsetRecognizer {
/**
* Get the IANA name of this charset.
* @return the charset name.
*/
abstract String getName();
/**
* Get the ISO language code for this charset.
* @return the language code, or null if the language cannot be determined.
*/
public String getLanguage()
{
return null;
}
/**
* Test the match of this charset with the input text data
* which is obtained via the CharsetDetector object.
*
* @param det The CharsetDetector, which contains the input text
* to be checked for being in this charset.
* @return A CharsetMatch object containing details of match
* with this charset, or null if there was no match.
*/
abstract CharsetMatch match(CharsetDetector det);
}
================================================
FILE: src/main/java/org/billthefarmer/editor/Editor.java
================================================
///////////////////////////////////////////////////////////////////////////////
//
// Editor - Text editor for Android
//
// Copyright © 2017 Bill Farmer
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
// Bill Farmer william j farmer [at] yahoo [dot] co [dot] uk.
//
////////////////////////////////////////////////////////////////////////////////
package org.billthefarmer.editor;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintManager;
import android.text.Editable;
import android.text.Html;
import android.text.InputType;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.ActionMode;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.SearchView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.support.v4.content.FileProvider;
import com.ibm.icu.text.CharsetDetector;
import com.ibm.icu.text.CharsetMatch;
import org.commonmark.node.*;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@SuppressWarnings("deprecation")
public class Editor extends Activity
{
public final static String TAG = "Editor";
public final static String PATH = "path";
public final static String EDIT = "edit";
public final static String MATCH = "match";
public final static String CHANGED = "changed";
public final static String CONTENT = "content";
public final static String MODIFIED = "modified";
public final static String MONOSPACE = "monospace";
public final static String PREF_FILE = "pref_file";
public final static String PREF_HIGH = "pref_high";
public final static String PREF_PATHS = "pref_paths";
public final static String PREF_SAVE = "pref_save";
public final static String PREF_LAST = "pref_last";
public final static String PREF_VIEW = "pref_view";
public final static String PREF_SIZE = "pref_size";
public final static String PREF_SUGGEST = "pref_suggest";
public final static String PREF_THEME = "pref_theme";
public final static String PREF_TYPE = "pref_type";
public final static String PREF_WRAP = "pref_wrap";
public final static String DOCUMENTS = "Documents";
public final static String FOLDER = "Folder";
public final static String UTF_8 = "UTF-8";
public final static String NEW_FILE = "Untitled.txt";
public final static String EDIT_FILE = "Editor.txt";
public final static String HTML_FILE = "Editor.html";
public final static String TEXT_HTML = "text/html";
public final static String TEXT_PLAIN = "text/plain";
public final static String TEXT_WILD = "text/*";
public final static Pattern PATTERN_CHARS =
Pattern.compile("[\\(\\)\\[\\]\\{\\}\\<\\>\"'`]");
public final static String BRACKET_CHARS = "([{<";
public final static String HTML_HEAD =
"\n\n\n \n" +
" \n\n\n";
public final static String HTML_TAIL = "\n\n\n";
public final static String FILE_PROVIDER =
"org.billthefarmer.editor.fileprovider";
public final static String OPEN_NEW =
"org.billthefarmer.editor.OPEN_NEW";
public final static String PACKAGE = "package:";
public final static String CC_EXT =
"\\.(c(c|pp|xx|\\+\\+)?|dart|go|h|java|js|kt|m|py|swift|rs)";
public final static String HTML_EXT = "\\.html?";
public final static String CSS_EXT = "\\.css?";
public final static String ORG_EXT = "\\.org";
public final static String MD_EXT = "\\.md";
public final static String SH_EXT = "\\.sh";
// Syntax patterns
public final static Pattern KEYWORDS = Pattern.compile
("\\b(abstract|and|arguments|as(m|sert|sociativity|ync)?|await|auto|" +
"base|break|" +
"case|catch|chan|char|class|con(st|tinue|venience)|continue|" +
"covariant|crate|" +
"de(bugger|f|fault|fer|fered|in|init|l|lete)|didset|do(ne)?|" +
"dyn(amic)?" +
"(type)?|el(if|se)|enum|esac|eval|ex(cept|ec|plicit|port|" +
"tends|tension|tern)|factory|fal(lthrough|se)|fi(nal|nally)?|fn|for|" +
"friend|from|fun(c(tion)?)?|get|global|go(to)?|hide|if|" +
"im(pl|plements|port)|in(fix|it|line|out|stanceof|terface|" +
"ternal)?|is|la(mbda|te|zy)|left|let|library|lo(cal|oop)|ma(p|" +
"tch)|mut(able|ating)|" +
"namespace|native|new|nil|none|nonmutating|not|null|of|on|" +
"operator|optional|or|override|package|part|pass|postfix|" +
"pre(cedence|fix)|print|private|prot(ected|ocol)|pub(lic)?|" +
"raise|range|re(f|gister|quired|throw|turn)|right|se(lect|aled|lf)|" +
"set|show|signed|sizeof|static|strictfp|struct|subscript|super|" +
"switch|sync(hronized)?|template|th(en|is|rows?)|transient|" +
"true|try|type(alias|def|id|name|of)?|un(ion|owned|safe|signed)|" +
"us(e|ing)|va(l|r)|virtual|void|volatile|weak|wh(en|ere|ile)|" +
"willset|with|yield)\\b", Pattern.MULTILINE);
public final static Pattern TYPES = Pattern.compile
("\\b(j?bool(ean)?|(u|j)?(byte|char|double|float|int(eger)?|" +
"long|short))\\b", Pattern.MULTILINE);
public final static Pattern ANNOTATION =
Pattern.compile("@\\b[A-Za-z]+\\b", Pattern.MULTILINE);
public final static Pattern CC_COMMENT = Pattern.compile
("//.*$|(\"(?:\\\\[^\"]|\\\\\"|.)*?\")|(?s)/\\*.*?\\*/",
Pattern.MULTILINE);
public final static Pattern CLASS = Pattern.compile
("\\b[A-Z][A-Za-z0-9_]+\\b", Pattern.MULTILINE);
public final static Pattern CONSTANT = Pattern.compile
("\\b(([A-Z][A-Z0-9_]+)|(k[A-Z][A-Za-z0-9]+))\\b",
Pattern.MULTILINE);
public final static Pattern OPERATOR = Pattern.compile
("[+-=:;<>|!%^&*/?]+", Pattern.MULTILINE);
public final static Pattern NUMBER = Pattern.compile
("\\b\\d+(\\.\\d*)?(e(\\+|\\-)?\\d+)?\\b",
Pattern.MULTILINE);
public final static Pattern QUOTED = Pattern.compile
// "'([^\\\\']+|\\\\([btnfr\"'\\\\]|" +
// "[0-3]?[0-7]{1,2}|u[0-9a-fA-F]{4}))*'|" +
("\"([^\\\\\"]+|\\\\([btnfr\"'\\\\]|" +
"[0-3]?[0-7]{1,2}|u[0-9a-fA-F]{4}))*\"",
Pattern.MULTILINE);
public final static Pattern HTML_TAGS = Pattern.compile
("\\b(html|base|head|link|meta|style|title|body|address|article|" +
"aside|footer|header|h\\d|hgroup|main|nav|section|blockquote|dd|" +
"dir|div|dl|dt|figcaption|figure|hr|li|main|ol|p|pre|ul|a|abbr|" +
"b|bdi|bdo|br|cite|code|data|dfn|em|i|kbd|mark|q|rb|rp|rt|rtc|" +
"ruby|s|samp|small|span|strong|sub|sup|time|tt|u|var|wbr|area|" +
"audio|img|map|track|video|applet|embed|iframe|noembed|object|" +
"param|picture|source|canvas|noscript|script|del|ins|caption|" +
"col|colgroup|table|tbody|td|tfoot|th|thead|tr|button|datalist|" +
"fieldset|form|input|label|legend|meter|optgroup|option|output|" +
"progress|select|textarea|details|dialog|menu|menuitem|summary|" +
"content|element|shadow|slot|template|acronym|applet|basefont|" +
"bgsound|big|blink|center|command|content|dir|element|font|" +
"frame|frameset|image|isindex|keygen|listing|marquee|menuitem|" +
"multicol|nextid|nobr|noembed|noframes|plaintext|shadow|spacer|" +
"strike|tt|xmp|doctype)\\b",
Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
public final static Pattern HTML_ATTRS = Pattern.compile
("\\b(accept|accesskey|action|align|allow|alt|async|" +
"auto(capitalize|complete|focus|play)|background|" +
"bgcolor|border|buffered|challenge|charset|checked|cite|" +
"class|code(base)?|color|cols|colspan|content(" +
"editable)?|contextmenu|controls|coords|crossorigin|" +
"csp|data|datetime|decoding|def(ault|er)|dir|dirname|" +
"disabled|download|draggable|dropzone|enctype|enterkeyhint|" +
"equiv|for|form(action|novalidate)?|headers|height|" +
"hidden|high|href(lang)?|http|icon|id|importance|" +
"inputmode|integrity|intrinsicsize|ismap|itemprop|keytype|" +
"kind|label|lang|language|list|loading|loop|low|manifest|" +
"max|maxlength|media|method|min|minlength|multiple|muted|" +
"name|novalidate|open|optimum|pattern|ping|placeholder|" +
"poster|preload|property|radiogroup|readonly|referrerpolicy|" +
"rel|required|reversed|rows|rowspan|sandbox|scope|scoped|" +
"selected|shape|size|sizes|slot|span|spellcheck|src|srcdoc|" +
"srclang|srcset|start|step|style|summary|tabindex|target|" +
"title|translate|type|usemap|value|width|wrap)\\b",
Pattern.MULTILINE);
public final static Pattern HTML_COMMENT =
Pattern.compile("", Pattern.MULTILINE);
public final static Pattern CSS_STYLES = Pattern.compile
("\\b(action|active|additive|adjust|after|align|all|alternates|" +
"animation|annotation|area|areas|as|asian|attachment|attr|" +
"auto|backdrop|backface|background|basis|before|behavior|" +
"bezier|bidi|blend|block|blur|border|both|bottom|box|break|" +
"brightness|calc|caps|caption|caret|cells|center|ch|change|" +
"character|charset|checked|child|circle|clamp|clear|clip|" +
"cm|collapse|color|column|columns|combine|composite|conic|" +
"content|contrast|count|counter|counters|cross|cubic|cue|" +
"cursor|decoration|default|deg|delay|dir|direction|" +
"disabled|display|dpcm|dpi|dppx|drop|duration|east|element|" +
"ellipse|em|emphasis|empty|enabled|end|env|events|ex|face|" +
"fade|fallback|family|feature|fill|filter|first|fit|flex|" +
"float|flow|focus|font|format|forms|fr|frames|fullscreen|" +
"function|gap|grad|gradient|grayscale|grid|grow|hanging|" +
"height|historical|hover|hsl|hsla|hue|hyphens|hz|image|import|" +
"in|increment|indent|indeterminate|index|inherit|initial|" +
"inline|inset|inside|invalid|invert|isolation|items|" +
"iteration|justify|khz|kerning|keyframes|lang|language|" +
"last|layout|leader|left|letter|ligatures|line|linear|link|" +
"list|local|margin|mask|matrix|matrix3d|max|media|min|" +
"minmax|mix|mm|mode|ms|name|namespace|negative|none|not|nth|" +
"numeric|object|of|offset|only|opacity|optical|optional|" +
"order|orientation|origin|ornaments|orphans|out|outline|" +
"outset|outside|overflow|override|pad|padding|page|path|pc|" +
"perspective|place|placeholder|play|pointer|polygon|" +
"position|prefix|property|pt|punctuation|px|q|quotes|rad|" +
"radial|radius|range|read|rect|relative|rem|rendering|repeat|" +
"repeating|required|reset|resize|revert|rgb|rgba|right|" +
"root|rotate|rotate3d|rotatex|rotatey|rotatez|row|rows|" +
"rule|s|saturate|scale|scale3d|scalex|scaley|scalez|scope|" +
"scroll|scrollbar|selection|self|sepia|set|settings|shadow|" +
"shape|shrink|side|size|sizing|skew|skewx|skewy|slice|" +
"slotted|snap|source|space|spacing|span|speak|src|start|" +
"state|static|steps|stop|stretch|style|styleset|stylistic|suffix|" +
"supports|swash|symbols|synthesis|system|tab|table|target|" +
"template|text|threshold|timing|top|touch|transform|" +
"transition|translate|translate3d|translatex|translatey|" +
"translatez|turn|type|underline|unicode|unset|upright|url|" +
"user|valid|values|var|variant|variation|vertical|vh|" +
"viewport|visibility|visited|vmax|vmin|vw|weight|white|" +
"widows|width|will|word|wrap|write|writing|x|y|z|zoom)\\b",
Pattern.MULTILINE);
public final static Pattern CSS_HEX = Pattern.compile
("#\\b[A-Fa-f0-9]+\\b", Pattern.MULTILINE);
public final static Pattern ORG_HEADER = Pattern.compile
("(^\\*+ +.+$)|(^#\\+.+$)", Pattern.MULTILINE);
public final static Pattern ORG_LINK = Pattern.compile
("\\[\\[.*?\\]\\]", Pattern.MULTILINE);
public final static Pattern ORG_EMPH = Pattern.compile
("(([*~/+=]+)\\b(\\w| )+?\\b\\2)|(\\b(_{1,2})(\\w| )+?\\5\\b)",
Pattern.MULTILINE);
public final static Pattern ORG_COMMENT = Pattern.compile
("(^# .*$)|(@@.*?@@)", Pattern.MULTILINE);
public final static Pattern MD_HEADER = Pattern.compile
("(^.+\\s+-+$)|(^.+\\s+=+$)|(^#+ +.+$)", Pattern.MULTILINE);
public final static Pattern MD_LINK = Pattern.compile
("(\\!?\\[.+\\] *\\(.+\\))|(!?\\[.+\\] *\\[.+\\])|" +
"( *\\[.+\\]: +.+$)", Pattern.MULTILINE);
public final static Pattern MD_EMPH = Pattern.compile
("(([*~]{1,2})\\b(\\w| )+?\\b\\2)|(\\b(_{1,2})(\\w| )+?\\5\\b)",
Pattern.MULTILINE);
public final static Pattern MD_CODE = Pattern.compile
("(^ {4,}.+$)|(`.+?`)", Pattern.MULTILINE);
public final static Pattern SH_VAR = Pattern.compile
("(\\$\\b\\w+\\b)|(\\$\\{.+?\\})|(\\$\\(.+?\\))", Pattern.MULTILINE);
public final static Pattern SH_COMMENT = Pattern.compile
("#.*$", Pattern.MULTILINE);
public final static Pattern MODE_PATTERN = Pattern.compile
("^\\S+\\s+ed:(.+)$", Pattern.MULTILINE);
public final static Pattern OPTION_PATTERN = Pattern.compile
("(\\s+(no)?(vw|ww|sg|cs|hs|th|ts|tf)(:\\w)?)", Pattern.MULTILINE);
public final static Pattern WORD_PATTERN = Pattern.compile
("\\w+", Pattern.MULTILINE);
public final static int LAST_SIZE = 256;
public final static int MENU_SIZE = 192;
public final static int FIRST_SIZE = 256;
public final static int TOO_LARGE = 524288;
public final static int FOLDER_OFFSET = 0x7d000000;
public final static int POSITION_DELAY = 128;
public final static int UPDATE_DELAY = 128;
public final static int FIND_DELAY = 128;
public final static int MAX_PATHS = 10;
public final static int REQUEST_OPEN = 1;
public final static int OPEN_DOCUMENT = 1;
public final static int CREATE_DOCUMENT = 2;
public final static int LIGHT = 1;
public final static int DARK = 2;
public final static int SYSTEM = 3;
public final static int WHITE = 4;
public final static int BLACK = 5;
public final static int RETRO = 6;
private final static int TINY = 8;
private final static int SMALL = 12;
private final static int MEDIUM = 18;
private final static int LARGE = 24;
private final static int HUGE = 32;
private final static int NORMAL = 1;
private final static int MONO = 2;
private final static int SANS = 3;
private final static int SERIF = 4;
private final static int NO_SYNTAX = 0;
private final static int CC_SYNTAX = 1;
private final static int HTML_SYNTAX = 2;
private final static int CSS_SYNTAX = 3;
private final static int ORG_SYNTAX = 4;
private final static int MD_SYNTAX = 5;
private final static int SH_SYNTAX = 6;
private final static int DEF_SYNTAX = 7;
private Uri uri;
private File file;
private String path;
private Uri content;
private String match;
private EditText textView;
private TextView customView;
private MenuItem searchItem;
private SearchView searchView;
private ScrollView scrollView;
private Runnable updateHighlight;
private Runnable updateWordCount;
private ScaleGestureDetector scaleDetector;
private QueryTextListener queryTextListener;
private ExecutorService executor;
private Map pathMap;
private List removeList;
private boolean highlight = false;
private boolean last = false;
private boolean save = false;
private boolean edit = false;
private boolean view = false;
private boolean wrap = false;
private boolean suggest = true;
private boolean changed = false;
private long modified;
private int theme = LIGHT;
private int size = MEDIUM;
private int type = MONO;
private int syntax;
// onCreate
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(this);
String typefaces[] = getResources().getStringArray(R.array.typefaces);
List typeList = Arrays.asList(typefaces);
int monospace = typeList.indexOf(MONOSPACE);
save = preferences.getBoolean(PREF_SAVE, false);
view = preferences.getBoolean(PREF_VIEW, true);
last = preferences.getBoolean(PREF_LAST, false);
wrap = preferences.getBoolean(PREF_WRAP, false);
suggest = preferences.getBoolean(PREF_SUGGEST, true);
highlight = preferences.getBoolean(PREF_HIGH, false);
theme = preferences.getInt(PREF_THEME, LIGHT);
size = preferences.getInt(PREF_SIZE, MEDIUM);
type = preferences.getInt(PREF_TYPE, monospace);
Set pathSet = preferences.getStringSet(PREF_PATHS, null);
pathMap = new HashMap<>();
if (pathSet != null)
for (String path : pathSet)
pathMap.put(path, preferences.getInt(path, 0));
removeList = new ArrayList<>();
Configuration config = getResources().getConfiguration();
int night = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (theme)
{
case LIGHT:
setTheme(R.style.AppTheme);
break;
case DARK:
setTheme(R.style.AppDarkTheme);
break;
case SYSTEM:
switch (night)
{
case Configuration.UI_MODE_NIGHT_NO:
setTheme(R.style.AppTheme);
break;
case Configuration.UI_MODE_NIGHT_YES:
setTheme(R.style.AppDarkTheme);
break;
}
break;
case WHITE:
setTheme(R.style.AppWhiteTheme);
break;
case BLACK:
setTheme(R.style.AppBlackTheme);
break;
case RETRO:
setTheme(R.style.AppRetroTheme);
break;
}
if (wrap)
setContentView(R.layout.wrap);
else
setContentView(R.layout.edit);
textView = findViewById(R.id.text);
scrollView = findViewById(R.id.vscroll);
getActionBar().setSubtitle(match);
getActionBar().setCustomView(R.layout.custom);
getActionBar().setDisplayShowCustomEnabled(true);
customView = (TextView) getActionBar().getCustomView();
updateWordCount = () -> wordCountText();
if (savedInstanceState != null)
edit = savedInstanceState.getBoolean(EDIT);
if (!edit)
{
textView.setRawInputType(InputType.TYPE_NULL);
textView.setTextIsSelectable(true);
}
else if (!suggest)
textView.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_FLAG_MULTI_LINE |
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
setSizeAndTypeface(size, type);
executor = Executors.newSingleThreadExecutor();
setListeners();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
!Environment.isExternalStorageManager())
{
alertDialog(this, R.string.access, R.string.manageFiles,
R.string.access, R.string.cancel, (dialog, id) ->
{
switch (id)
{
case DialogInterface.BUTTON_POSITIVE:
Uri appId = Uri.parse(PACKAGE + BuildConfig.APPLICATION_ID);
Intent intent = new
Intent(android.provider.Settings.
ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
appId);
startActivity(intent);
break;
case DialogInterface.BUTTON_NEGATIVE:
break;
}
});
newFile();
return;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R &&
checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED)
{
alertDialog(this, R.string.files, R.string.manageFiles,
R.string.files, R.string.cancel, (dialog, id) ->
{
switch (id)
{
case DialogInterface.BUTTON_POSITIVE:
requestPermissions(new String[]
{Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_OPEN);
break;
case DialogInterface.BUTTON_NEGATIVE:
break;
}
});
newFile();
return;
}
Intent intent = getIntent();
Uri uri = intent.getData();
switch (intent.getAction())
{
case Intent.ACTION_EDIT:
case Intent.ACTION_VIEW:
if ((savedInstanceState == null) && (uri != null))
readFile(uri);
getActionBar().setDisplayHomeAsUpEnabled(true);
break;
case Intent.ACTION_SEND:
if (savedInstanceState == null)
{
// Get uri
uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
// Get text
String text = intent.getStringExtra(Intent.EXTRA_TEXT);
if (uri != null)
readFile(uri);
else if (text != null)
{
newFile(text);
changed = true;
}
else
defaultFile();
}
break;
case OPEN_NEW:
if (savedInstanceState == null)
{
newFile();
textView.postDelayed(() -> editClicked(null), UPDATE_DELAY);
}
break;
case Intent.ACTION_MAIN:
if (savedInstanceState == null)
{
if (last)
lastFile();
else
defaultFile();
}
break;
}
}
// setListeners
private void setListeners()
{
scaleDetector = new ScaleGestureDetector(this, new ScaleListener());
queryTextListener = new QueryTextListener();
if (textView != null)
{
textView.addTextChangedListener(new TextWatcher()
{
// afterTextChanged
@Override
public void afterTextChanged(Editable s)
{
if (!changed)
{
changed = true;
invalidateOptionsMenu();
}
if (updateHighlight != null)
{
textView.removeCallbacks(updateHighlight);
textView.postDelayed(updateHighlight, UPDATE_DELAY);
}
if (updateWordCount != null)
{
textView.removeCallbacks(updateWordCount);
textView.postDelayed(updateWordCount, UPDATE_DELAY);
}
}
// beforeTextChanged
@Override
public void beforeTextChanged(CharSequence s,
int start,
int count,
int after)
{
if (searchItem != null &&
searchItem.isActionViewExpanded())
{
final CharSequence query = searchView.getQuery();
textView.postDelayed(() ->
{
if (searchItem != null &&
searchItem.isActionViewExpanded())
{
if (query != null)
searchView.setQuery(query, false);
}
}, UPDATE_DELAY);
}
}
// onTextChanged
@Override
public void onTextChanged(CharSequence s,
int start,
int before,
int count) {}
});
// onFocusChange
textView.setOnFocusChangeListener((v, hasFocus) ->
{
// Hide keyboard
InputMethodManager manager = (InputMethodManager)
getSystemService(INPUT_METHOD_SERVICE);
if (!hasFocus)
manager.hideSoftInputFromWindow(v.getWindowToken(), 0);
if (updateHighlight != null)
{
textView.removeCallbacks(updateHighlight);
textView.postDelayed(updateHighlight, UPDATE_DELAY);
}
});
// onLongClick
textView.setOnLongClickListener(v ->
{
// Do nothing if already editable
if (edit)
return false;
// Get scroll position
int y = scrollView.getScrollY();
// Get height
int height = scrollView.getHeight();
// Get width
int width = scrollView.getWidth();
// Get offset
int line = textView.getLayout()
.getLineForVertical(y + height / 2);
int offset = textView.getLayout()
.getOffsetForHorizontal(line, width / 2);
// Set cursor
textView.setSelection(offset);
// Set editable with or without suggestions
if (suggest)
textView
.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_FLAG_MULTI_LINE);
else
textView
.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_FLAG_MULTI_LINE |
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
// Update boolean
edit = true;
// Restart
recreate(this);
return false;
});
textView.getViewTreeObserver().addOnGlobalLayoutListener(() ->
{
if (updateHighlight != null)
{
textView.removeCallbacks(updateHighlight);
textView.postDelayed(updateHighlight, UPDATE_DELAY);
}
});
}
if (scrollView != null)
{
// onScrollChange
scrollView.getViewTreeObserver().addOnScrollChangedListener(() ->
{
if (updateHighlight != null)
{
textView.removeCallbacks(updateHighlight);
textView.postDelayed(updateHighlight, UPDATE_DELAY);
}
});
}
}
// onRestoreInstanceState
@Override
public void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
path = savedInstanceState.getString(PATH);
edit = savedInstanceState.getBoolean(EDIT);
changed = savedInstanceState.getBoolean(CHANGED);
match = savedInstanceState.getString(MATCH);
modified = savedInstanceState.getLong(MODIFIED);
content = savedInstanceState.getParcelable(CONTENT);
invalidateOptionsMenu();
file = new File(path);
uri = Uri.fromFile(file);
if (content != null)
setTitle(FileUtils.getDisplayName(this, content, null, null));
else
setTitle(uri.getLastPathSegment());
if (match == null)
match = UTF_8;
getActionBar().setSubtitle(match);
checkHighlight();
if (file.lastModified() > modified)
alertDialog(this, R.string.appName, R.string.changedReload,
R.string.reload, R.string.cancel, (dialog, id) ->
{
switch (id)
{
case DialogInterface.BUTTON_POSITIVE:
readFile(uri);
}
});
}
// onPause
@Override
public void onPause()
{
super.onPause();
// Save current path
savePath(path);
// Stop highlighting
textView.removeCallbacks(updateHighlight);
textView.removeCallbacks(updateWordCount);
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(PREF_SAVE, save);
editor.putBoolean(PREF_VIEW, view);
editor.putBoolean(PREF_LAST, last);
editor.putBoolean(PREF_WRAP, wrap);
editor.putBoolean(PREF_SUGGEST, suggest);
editor.putBoolean(PREF_HIGH, highlight);
editor.putInt(PREF_THEME, theme);
editor.putInt(PREF_SIZE, size);
editor.putInt(PREF_TYPE, type);
editor.putString(PREF_FILE, path);
// Add the set of recent files
editor.putStringSet(PREF_PATHS, pathMap.keySet());
// Add a position for each file
for (String path : pathMap.keySet())
editor.putInt(path, pathMap.get(path));
// Remove the old ones
for (String path : removeList)
editor.remove(path);
editor.apply();
// Save file
if (changed && save)
saveFile();
}
// onSaveInstanceState
@Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putParcelable(CONTENT, content);
outState.putLong(MODIFIED, modified);
outState.putBoolean(CHANGED, changed);
outState.putString(MATCH, match);
outState.putBoolean(EDIT, edit);
outState.putString(PATH, path);
}
// onCreateOptionsMenu
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
// onPrepareOptionsMenu
@Override
public boolean onPrepareOptionsMenu(Menu menu)
{
// Set up search view
searchItem = menu.findItem(R.id.search);
searchView = (SearchView) searchItem.getActionView();
// Set up search view options and listener
if (searchView != null)
{
searchView.setSubmitButtonEnabled(true);
searchView.setImeOptions(EditorInfo.IME_ACTION_GO);
searchView.setOnQueryTextListener(queryTextListener);
}
// Show find all item
if (menu.findItem(R.id.search).isActionViewExpanded())
menu.findItem(R.id.findAll).setVisible(true);
else
menu.findItem(R.id.findAll).setVisible(false);
menu.findItem(R.id.edit).setVisible(!edit);
menu.findItem(R.id.view).setVisible(edit);
menu.findItem(R.id.save).setVisible(changed);
menu.findItem(R.id.viewFile).setChecked(view);
menu.findItem(R.id.openLast).setChecked(last);
menu.findItem(R.id.autoSave).setChecked(save);
menu.findItem(R.id.wrap).setChecked(wrap);
menu.findItem(R.id.suggest).setChecked(suggest);
menu.findItem(R.id.highlight).setChecked(highlight);
switch (theme)
{
case LIGHT:
menu.findItem(R.id.light).setChecked(true);
break;
case DARK:
menu.findItem(R.id.dark).setChecked(true);
break;
case SYSTEM:
menu.findItem(R.id.system).setChecked(true);
break;
case WHITE:
menu.findItem(R.id.white).setChecked(true);
break;
case BLACK:
menu.findItem(R.id.black).setChecked(true);
break;
case RETRO:
menu.findItem(R.id.retro).setChecked(true);
break;
}
switch (size)
{
case SMALL:
menu.findItem(R.id.small).setChecked(true);
break;
case MEDIUM:
menu.findItem(R.id.medium).setChecked(true);
break;
case LARGE:
menu.findItem(R.id.large).setChecked(true);
break;
}
// Get the charsets
Set keySet = Charset.availableCharsets().keySet();
// Get the submenu
MenuItem item = menu.findItem(R.id.charset);
item.setTitle(match);
SubMenu sub = item.getSubMenu();
sub.clear();
// Add charsets contained in both sets
sub.add(Menu.NONE, R.id.charsetItem, Menu.NONE, R.string.detect);
for (String key: keySet)
sub.add(Menu.NONE, R.id.charsetItem, Menu.NONE, key);
// Get the typefaces
String typefaces[] = getResources().getStringArray(R.array.typefaces);
item = menu.findItem(R.id.typeface);
sub = item.getSubMenu();
sub.clear();
// Add typefaces
for (String typeface: typefaces)
sub.add(Menu.NONE, R.id.typefaceItem, Menu.NONE, typeface);
sub.getItem(type).setCheckable(true);
sub.getItem(type).setChecked(true);
// Get a list of recent files
List list = new ArrayList<>();
Map map = new HashMap<>();
// Get the last modified dates
for (String path: pathMap.keySet())
{
File file = new File(path);
// Check it exists
if (!file.exists())
continue;
long last = file.lastModified();
list.add(last);
map.put(last, path);
}
// Sort in reverse order
Collections.sort(list);
Collections.reverse(list);
// Get the submenu
item = menu.findItem(R.id.openRecent);
sub = item.getSubMenu();
sub.clear();
// Add the recent files
for (long date : list)
{
String path = map.get(date);
// Remove path prefix
CharSequence name =
path.replaceFirst(Environment
.getExternalStorageDirectory()
.getPath() + File.separator, "");
// Create item
sub.add(Menu.NONE, R.id.fileItem, Menu.NONE, TextUtils.ellipsize
(name, new TextPaint(), MENU_SIZE,
TextUtils.TruncateAt.MIDDLE))
// Use condensed title to save path as API doesn't
// work as documented
.setTitleCondensed(name);
}
// Add clear list item
sub.add(Menu.NONE, R.id.clearList, Menu.NONE, R.string.clearList);
return true;
}
// onOptionsItemSelected
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case android.R.id.home:
onBackPressed();
break;
case R.id.newFile:
newFile();
break;
case R.id.edit:
editClicked(item);
break;
case R.id.view:
viewClicked(item);
break;
case R.id.open:
openFile();
break;
case R.id.save:
saveCheck();
break;
case R.id.saveAs:
saveAs();
break;
case R.id.clearList:
clearList();
break;
case R.id.findAll:
findAll();
break;
case R.id.goTo:
goTo();
break;
case R.id.print:
print();
break;
case R.id.viewMarkdown:
viewMarkdown();
break;
case R.id.viewFile:
viewFileClicked(item);
break;
case R.id.openLast:
openLastClicked(item);
break;
case R.id.autoSave:
autoSaveClicked(item);
break;
case R.id.wrap:
wrapClicked(item);
break;
case R.id.suggest:
suggestClicked(item);
break;
case R.id.highlight:
highlightClicked(item);
break;
case R.id.light:
lightClicked(item);
break;
case R.id.dark:
darkClicked(item);
break;
case R.id.system:
systemClicked(item);
break;
case R.id.white:
whiteClicked(item);
break;
case R.id.black:
blackClicked(item);
break;
case R.id.retro:
retroClicked(item);
break;
case R.id.small:
smallClicked(item);
break;
case R.id.medium:
mediumClicked(item);
break;
case R.id.large:
largeClicked(item);
break;
case R.id.about:
aboutClicked();
break;
case R.id.fileItem:
openRecent(item);
break;
case R.id.charsetItem:
setCharset(item);
break;
case R.id.typefaceItem:
setTypeface(item);
break;
}
// Close text search
if (searchItem != null && searchItem.isActionViewExpanded() &&
item.getItemId() != R.id.findAll)
searchItem.collapseActionView();
return true;
}
// onBackPressed
@Override
public void onBackPressed()
{
// Close text search
if (searchItem != null && searchItem.isActionViewExpanded())
{
searchItem.collapseActionView();
return;
}
if (changed)
alertDialog(this, R.string.appName, R.string.modified,
R.string.save, R.string.discard, (dialog, id) ->
{
switch (id)
{
case DialogInterface.BUTTON_POSITIVE:
saveFile();
finish();
break;
case DialogInterface.BUTTON_NEGATIVE:
changed = false;
finish();
break;
}
});
else
finish();
}
// onActivityResult
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data)
{
if (resultCode == RESULT_CANCELED)
return;
switch (requestCode)
{
case OPEN_DOCUMENT:
content = data.getData();
readFile(content);
break;
case CREATE_DOCUMENT:
content = data.getData();
setTitle(FileUtils.getDisplayName(this, content, null, null));
saveFile();
break;
}
}
// dispatchTouchEvent
@Override
public boolean dispatchTouchEvent(MotionEvent event)
{
scaleDetector.onTouchEvent(event);
return super.dispatchTouchEvent(event);
}
// onKeyDown
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
// Check Ctrl key
if (event.isCtrlPressed())
{
switch (keyCode)
{
// Edit, View
case KeyEvent.KEYCODE_E:
if (event.isShiftPressed())
viewClicked(null);
else
editClicked(null);
break;
// Search
case KeyEvent.KEYCODE_F:
if (event.isShiftPressed())
searchItem.collapseActionView();
else
searchItem.expandActionView();
// Find next
if (event.isAltPressed() &&
searchItem.isActionViewExpanded())
queryTextListener.onQueryTextSubmit
(searchView.getQuery().toString());
break;
// Goto
case KeyEvent.KEYCODE_G:
goTo();
break;
// Menu
case KeyEvent.KEYCODE_M:
openOptionsMenu();
break;
// New
case KeyEvent.KEYCODE_N:
newFile();
break;
// Open
case KeyEvent.KEYCODE_O:
openFile();
break;
// Print
case KeyEvent.KEYCODE_P:
print();
break;
// Save, Save as
case KeyEvent.KEYCODE_S:
if (event.isShiftPressed())
saveAs();
else
saveCheck();
break;
// Increase text size
case KeyEvent.KEYCODE_PLUS:
case KeyEvent.KEYCODE_EQUALS:
size += 2;
size = Math.max(TINY, Math.min(size, HUGE));
textView.setTextSize(size);
break;
// Decrease text size
case KeyEvent.KEYCODE_MINUS:
size -= 2;
size = Math.max(TINY, Math.min(size, HUGE));
textView.setTextSize(size);
break;
default:
return super.onKeyDown(keyCode, event);
}
return true;
}
else
{
switch (keyCode)
{
// Find next
case KeyEvent.KEYCODE_F3:
if (searchItem.isActionViewExpanded())
queryTextListener.onQueryTextSubmit
(searchView.getQuery().toString());
break;
// Menu
case KeyEvent.KEYCODE_F10:
openOptionsMenu();
break;
default:
return super.onKeyDown(keyCode, event);
}
return true;
}
}
// editClicked
private void editClicked(MenuItem item)
{
// Get scroll position
int y = scrollView.getScrollY();
// Get height
int height = scrollView.getHeight();
// Get width
int width = scrollView.getWidth();
// Get offset
int line = textView.getLayout()
.getLineForVertical(y + height / 2);
int offset = textView.getLayout()
.getOffsetForHorizontal(line, width / 2);
// Set cursor
textView.setSelection(offset);
// Set editable with or without suggestions
if (suggest)
textView.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_FLAG_MULTI_LINE);
else
textView.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_FLAG_MULTI_LINE |
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
// Update boolean
edit = true;
// Recreate
recreate(this);
}
// viewClicked
private void viewClicked(MenuItem item)
{
// Set read only
textView.setRawInputType(InputType.TYPE_NULL);
textView.setTextIsSelectable(true);
textView.clearFocus();
// Update boolean
edit = false;
// Update menu
invalidateOptionsMenu();
}
// newFile
private void newFile()
{
// Check if file changed
if (changed)
alertDialog(this, R.string.newFile, R.string.modified,
R.string.save, R.string.discard, (dialog, id) ->
{
switch (id)
{
case DialogInterface.BUTTON_POSITIVE:
saveFile();
newFile(null);
break;
case DialogInterface.BUTTON_NEGATIVE:
newFile(null);
break;
}
invalidateOptionsMenu();
});
else
newFile(null);
invalidateOptionsMenu();
}
// newFile
private void newFile(String text)
{
textView.setText("");
changed = false;
file = getNewFile();
uri = Uri.fromFile(file);
path = uri.getPath();
content = null;
if (text != null)
textView.append(text);
setTitle(uri.getLastPathSegment());
match = UTF_8;
getActionBar().setSubtitle(match);
}
// getNewFile
private static File getNewFile()
{
File documents = new
File(Environment.getExternalStorageDirectory(), DOCUMENTS);
return new File(documents, NEW_FILE);
}
// getDefaultFile
private static File getDefaultFile()
{
File documents = new
File(Environment.getExternalStorageDirectory(), DOCUMENTS);
return new File(documents, EDIT_FILE);
}
// defaultFile
private void defaultFile()
{
file = getDefaultFile();
uri = Uri.fromFile(file);
path = uri.getPath();
content = null;
if (file.exists())
readFile(uri);
else
{
setTitle(uri.getLastPathSegment());
match = UTF_8;
getActionBar().setSubtitle(match);
}
}
// lastFile
private void lastFile()
{
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(this);
String path = preferences.getString(PREF_FILE, "");
if (path.isEmpty())
{
defaultFile();
return;
}
file = new File(path);
uri = Uri.fromFile(file);
path = uri.getPath();
if (file.exists())
readFile(uri);
else
{
setTitle(uri.getLastPathSegment());
match = UTF_8;
getActionBar().setSubtitle(match);
}
}
// setCharset
private void setCharset(MenuItem item)
{
match = item.getTitle().toString();
getActionBar().setSubtitle(match);
}
// setTypeface
private void setTypeface(MenuItem item)
{
String name = item.getTitle().toString();
Typeface typeface = Typeface.create(name, Typeface.NORMAL);
textView.setTypeface(typeface);
item.setChecked(true);
String typefaces[] = getResources().getStringArray(R.array.typefaces);
List list = Arrays.asList(typefaces);
type = list.indexOf(name);
}
// alertDialog
private static void alertDialog(Context context, int title, int message,
int positiveButton, int negativeButton,
DialogInterface.OnClickListener listener)
{
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(title);
builder.setMessage(message);
// Add the buttons
builder.setPositiveButton(positiveButton, listener);
builder.setNegativeButton(negativeButton, listener);
// Create the AlertDialog
builder.show();
}
// alertDialog
private static void alertDialog(Context context, int title,
String message, int neutralButton)
{
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(title);
builder.setMessage(message);
// Add the buttons
builder.setNeutralButton(neutralButton, null);
// Create the AlertDialog
builder.show();
}
// savePath
private void savePath(String path)
{
if (path == null)
return;
// Save the current position
pathMap.put(path, scrollView.getScrollY());
// Get a list of files
List list = new ArrayList<>();
Map map = new HashMap<>();
for (String name: pathMap.keySet())
{
File file = new File(name);
// Add to remove list if non existant
if (!file.exists())
{
removeList.add(name);
continue;
}
list.add(file.lastModified());
map.put(file.lastModified(), name);
}
// Remove non existant entries
for (String name: removeList)
pathMap.remove(name);
// Sort in reverse order
Collections.sort(list);
Collections.reverse(list);
int count = 0;
for (long date : list)
{
String name = map.get(date);
// Remove old files
if (count >= MAX_PATHS)
{
pathMap.remove(name);
removeList.add(name);
}
count++;
}
}
// openRecent
private void openRecent(MenuItem item)
{
// Get path from condensed title
String name = item.getTitleCondensed().toString();
File file = new File(name);
// Check absolute file
if (!file.isAbsolute())
file = new File(Environment.getExternalStorageDirectory(), name);
// Check it exists
if (file.exists())
{
Uri uri = Uri.fromFile(file);
if (changed)
alertDialog(this, R.string.openRecent, R.string.modified,
R.string.save, R.string.discard, (dialog, id) ->
{
switch (id)
{
case DialogInterface.BUTTON_POSITIVE:
saveFile();
startActivity(new Intent(Intent.ACTION_EDIT, uri,
this, Editor.class));
break;
case DialogInterface.BUTTON_NEGATIVE:
startActivity(new Intent(Intent.ACTION_EDIT, uri,
this, Editor.class));
break;
}
});
else
// New instance
startActivity(new Intent(Intent.ACTION_EDIT, uri,
this, Editor.class));
}
}
// saveAs
private void saveAs()
{
// Remove path prefix
String name =
path.replaceFirst(Environment
.getExternalStorageDirectory()
.getPath() + File.separator, "");
// Open dialog
saveAsDialog(this, name, (dialog, id) ->
{
switch (id)
{
case DialogInterface.BUTTON_POSITIVE:
EditText text = ((Dialog) dialog).findViewById(R.id.pathText);
String string = text.getText().toString();
// Ignore empty string
if (string.isEmpty())
return;
file = new File(string);
// Check absolute file
if (!file.isAbsolute())
file = new
File(Environment.getExternalStorageDirectory(), string);
// Check uri
uri = Uri.fromFile(file);
Uri newUri = Uri.fromFile(getNewFile());
if (newUri.getPath().equals(uri.getPath()))
{
saveAs();
return;
}
// Check exists
if (file.exists())
alertDialog(this, R.string.appName,
R.string.changedOverwrite,
R.string.overwrite, R.string.cancel, (d, b) ->
{
switch (b)
{
case DialogInterface.BUTTON_POSITIVE:
// Set interface title
setTitle(uri.getLastPathSegment());
path = file.getPath();
saveFile();
break;
}
});
else
{
// Set interface title
setTitle(uri.getLastPathSegment());
path = file.getPath();
content = null;
saveFile();
}
break;
case DialogInterface.BUTTON_NEUTRAL:
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.setType(TEXT_WILD);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.putExtra(Intent.EXTRA_TITLE, uri.getLastPathSegment());
startActivityForResult(intent, CREATE_DOCUMENT);
break;
}
});
}
// saveAsDialog
private static void saveAsDialog(Context context, String path,
DialogInterface.OnClickListener listener)
{
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.save);
builder.setMessage(R.string.choose);
// Add the buttons
builder.setPositiveButton(R.string.save, listener);
builder.setNegativeButton(R.string.cancel, listener);
builder.setNeutralButton(R.string.storage, listener);
// Create edit text
LayoutInflater inflater = (LayoutInflater) builder.getContext()
.getSystemService(LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.save_path, null);
builder.setView(view);
// Create the AlertDialog
AlertDialog dialog = builder.show();
TextView text = dialog.findViewById(R.id.pathText);
text.setText(path);
}
// clearList
private void clearList()
{
for (String path : pathMap.keySet())
removeList.add(path);
pathMap.clear();
}
// findAll
public void findAll()
{
// Get search string
String search = searchView.getQuery().toString();
doFind(this, search);
}
// goTo
public void goTo()
{
gotoDialog((seekBar, progress) ->
{
int height = textView.getHeight();
int pos = progress * height / seekBar.getMax();
// Scroll to it
scrollView.smoothScrollTo(0, pos);
});
}
// OnSeekBarChangeListener
public interface OnSeekBarChangeListener
{
abstract void onProgressChanged(SeekBar seekBar, int progress);
}
// GotoDialog
private void gotoDialog(OnSeekBarChangeListener listener)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.goTo);
// Add the buttons
builder.setNegativeButton(R.string.cancel, null);
// Create seek bar
LayoutInflater inflater = (LayoutInflater) builder.getContext()
.getSystemService(LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.seek_bar, null);
builder.setView(view);
// Create the AlertDialog
AlertDialog dialog = builder.show();
SeekBar seekBar = dialog.findViewById(R.id.seekBar);
int height = textView.getHeight();
int progress = scrollView.getScrollY() * seekBar.getMax() / height;
seekBar.setProgress(progress);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
{
@Override
public void onProgressChanged(SeekBar seekBar,
int progress,
boolean fromUser)
{
if (fromUser)
listener.onProgressChanged(seekBar, progress);
}
@Override
public void onStartTrackingTouch (SeekBar seekBar) {}
@Override
public void onStopTrackingTouch (SeekBar seekBar)
{
dialog.dismiss();
}
});
}
// print
@SuppressWarnings("deprecation")
private void print()
{
WebView webView = new WebView(this);
webView.setWebViewClient(new WebViewClient()
{
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
return false;
}
@Override
public void onPageFinished(WebView view, String url)
{
// Get a PrintManager instance
PrintManager printManager = (PrintManager)
getSystemService(PRINT_SERVICE);
String jobName = getString(R.string.appName) + " Document";
// Get a print adapter instance
PrintDocumentAdapter printAdapter =
view.createPrintDocumentAdapter(jobName);
// Create a print job with name and adapter instance
printManager
.print(jobName, printAdapter,
new PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.ISO_A4)
.build());
}
});
String htmlDocument =
HTML_HEAD + Html.toHtml(textView.getText()) + HTML_TAIL;
webView.loadData(htmlDocument, TEXT_HTML, UTF_8);
}
// viewMarkdown
private void viewMarkdown()
{
String text = textView.getText().toString();
// Use commonmark
Parser parser = Parser.builder().build();
Node document = parser.parse(text);
HtmlRenderer renderer = HtmlRenderer.builder().build();
String html = renderer.render(document);
File file = new File(getCacheDir(), HTML_FILE);
file.deleteOnExit();
try (FileWriter writer = new FileWriter(file))
{
// Add HTML header and footer to make a valid page.
writer.write(HTML_HEAD);
writer.write(html);
writer.write(HTML_TAIL);
}
catch (Exception e)
{
e.printStackTrace();
}
try
{
// Get file provider uri
Uri uri = FileProvider.getUriForFile
(this, FILE_PROVIDER, file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, TEXT_HTML);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
}
catch (Exception e)
{
e.printStackTrace();
}
}
// viewFileClicked
private void viewFileClicked(MenuItem item)
{
view = !view;
item.setChecked(view);
}
// openLastClicked
private void openLastClicked(MenuItem item)
{
last = !last;
item.setChecked(last);
}
// autoSaveClicked
private void autoSaveClicked(MenuItem item)
{
save = !save;
item.setChecked(save);
}
// wrapClicked
private void wrapClicked(MenuItem item)
{
wrap = !wrap;
item.setChecked(wrap);
recreate(this);
}
// suggestClicked
private void suggestClicked(MenuItem item)
{
suggest = !suggest;
item.setChecked(suggest);
if (suggest)
textView.setRawInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_FLAG_MULTI_LINE);
else
textView.setRawInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_FLAG_MULTI_LINE |
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
recreate(this);
}
// highlightClicked
private void highlightClicked(MenuItem item)
{
highlight = !highlight;
item.setChecked(highlight);
checkHighlight();
}
// lightClicked
private void lightClicked(MenuItem item)
{
theme = LIGHT;
item.setChecked(true);
recreate(this);
}
// darkClicked
private void darkClicked(MenuItem item)
{
theme = DARK;
item.setChecked(true);
recreate(this);
}
// systemClicked
private void systemClicked(MenuItem item)
{
theme = SYSTEM;
item.setChecked(true);
recreate(this);
}
// whiteClicked
private void whiteClicked(MenuItem item)
{
theme = WHITE;
item.setChecked(true);
recreate(this);
}
// blackClicked
private void blackClicked(MenuItem item)
{
theme = BLACK;
item.setChecked(true);
recreate(this);
}
// retroClicked
private void retroClicked(MenuItem item)
{
theme = RETRO;
item.setChecked(true);
recreate(this);
}
// smallClicked
private void smallClicked(MenuItem item)
{
size = SMALL;
item.setChecked(true);
textView.setTextSize(size);
}
// mediumClicked
private void mediumClicked(MenuItem item)
{
size = MEDIUM;
item.setChecked(true);
textView.setTextSize(size);
}
// largeClicked
private void largeClicked(MenuItem item)
{
size = LARGE;
item.setChecked(true);
textView.setTextSize(size);
}
// setSizeAndTypeface
private void setSizeAndTypeface(int size, int type)
{
// Set size
textView.setTextSize(size);
// Set type
String names[] = getResources().getStringArray(R.array.typefaces);
Typeface typeface = Typeface.create(names[type], Typeface.NORMAL);
textView.setTypeface(typeface);
}
// aboutClicked
@SuppressWarnings("deprecation")
private void aboutClicked()
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.appName);
DateFormat dateFormat = DateFormat.getDateTimeInstance();
SpannableStringBuilder spannable =
new SpannableStringBuilder(getText(R.string.version));
Pattern pattern = Pattern.compile("%s");
Matcher matcher = pattern.matcher(spannable);
if (matcher.find())
spannable.replace(matcher.start(), matcher.end(),
BuildConfig.VERSION_NAME);
matcher.reset(spannable);
if (matcher.find())
spannable.replace(matcher.start(), matcher.end(),
dateFormat.format(BuildConfig.BUILT));
builder.setMessage(spannable);
// Add the button
builder.setPositiveButton(R.string.ok, null);
// Create the AlertDialog
Dialog dialog = builder.show();
// Set movement method
TextView text = dialog.findViewById(android.R.id.message);
if (text != null)
{
text.setTextAppearance(builder.getContext(),
android.R.style.TextAppearance_Small);
text.setMovementMethod(LinkMovementMethod.getInstance());
}
}
// recreate
private void recreate(Context context)
{
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.M)
recreate();
}
// openFile
private void openFile()
{
// Check if file changed
if (changed)
alertDialog(this, R.string.open, R.string.modified,
R.string.save, R.string.discard, (dialog, id) ->
{
switch (id)
{
case DialogInterface.BUTTON_POSITIVE:
saveFile();
getFile();
break;
case DialogInterface.BUTTON_NEGATIVE:
changed = false;
getFile();
break;
}
});
else
getFile();
}
// getFile
private void getFile()
{
// Open parent folder
File dir = file.getParentFile();
getFile(dir);
}
// getFile
private void getFile(File dir)
{
// Get list of files
List fileList = getList(dir);
if (fileList == null)
return;
// Get list of folders
List dirList = new ArrayList();
dirList.add(File.separator);
dirList.addAll(Uri.fromFile(dir).getPathSegments());
// Pop up dialog
openDialog(this, dirList, fileList, (dialog, which) ->
{
if (DialogInterface.BUTTON_NEUTRAL == which)
{
// Use storage
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType(TEXT_WILD);
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, OPEN_DOCUMENT);
return;
}
if (FOLDER_OFFSET <= which)
{
File file = new File(File.separator);
for (int i = 0; i <= which - FOLDER_OFFSET; i++)
file = new File(file, dirList.get(i));
if (file.isDirectory())
getFile(file);
return;
}
File selection = fileList.get(which);
if (selection.isDirectory())
getFile(selection);
else
readFile(Uri.fromFile(selection));
});
}
// getList
public static List getList(File dir)
{
List list = null;
File[] files = dir.listFiles();
// Check files
if (files == null)
{
// Create a list with just the parent folder and the
// external storage folder
list = new ArrayList();
if (dir.getParentFile() == null)
list.add(dir);
else
list.add(dir.getParentFile());
list.add(Environment.getExternalStorageDirectory());
return list;
}
// Sort the files
Arrays.sort(files);
// Create a list
list = new ArrayList(Arrays.asList(files));
// Add parent folder
if (dir.getParentFile() == null)
list.add(0, dir);
else
list.add(0, dir.getParentFile());
return list;
}
// openDialog
public static void openDialog(Context context, List dirList,
List fileList,
DialogInterface.OnClickListener listener)
{
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(FOLDER);
// Add the adapter
FileAdapter adapter = new FileAdapter(builder.getContext(), fileList);
builder.setAdapter(adapter, listener);
// Add storage button
builder.setNeutralButton(R.string.storage, listener);
// Add cancel button
builder.setNegativeButton(R.string.cancel, null);
// Create the Dialog
AlertDialog dialog = builder.create();
dialog.show();
// Find the title view
ViewGroup title = dialog.findViewById
(context.getResources().getIdentifier("title_template",
"id", "android"));
// Replace content with scroll view
title.removeAllViews();
HorizontalScrollView scroll = new
HorizontalScrollView(dialog.getContext());
title.addView(scroll);
// Add a row of folder buttons
LinearLayout layout = new LinearLayout(dialog.getContext());
scroll.addView(layout);
for (String dir: dirList)
{
Button button = new Button(dialog.getContext(), null,
android.R.attr.buttonStyleSmall);
button.setId(dirList.indexOf(dir) + FOLDER_OFFSET);
button.setText(dir);
button.setOnClickListener((v) ->
{
listener.onClick(dialog, v.getId());
dialog.dismiss();
});
layout.addView(button);
}
// Scroll to the end
scroll.postDelayed(() ->
{
scroll.fullScroll(View.FOCUS_RIGHT);
}, POSITION_DELAY);
}
// onRequestPermissionsResult
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions,
int[] grantResults)
{
switch (requestCode)
{
case REQUEST_OPEN:
for (int i = 0; i < grantResults.length; i++)
if (permissions[i].equals(Manifest.permission
.READ_EXTERNAL_STORAGE) &&
grantResults[i] == PackageManager.PERMISSION_GRANTED)
// Granted, open file
getFile();
break;
}
}
// readFile
private void readFile(Uri uri)
{
if (uri == null)
return;
long size = 0;
if (CONTENT.equalsIgnoreCase(uri.getScheme()))
size = FileUtils.getSize(this, uri, null, null);
else
{
File file = new File(uri.getPath());
size = file.length();
}
if (BuildConfig.DEBUG)
Log.d(TAG, "Size " + size);
if (size > TOO_LARGE)
{
String large = getString(R.string.tooLarge);
large = String.format(large, FileUtils.getReadableFileSize(size));
alertDialog(this, R.string.appName, large, R.string.ok);
return;
}
// Stop highlighting
textView.removeCallbacks(updateHighlight);
textView.removeCallbacks(updateWordCount);
if (BuildConfig.DEBUG)
Log.d(TAG, "Uri: " + uri);
// Attempt to resolve content uri
if (CONTENT.equalsIgnoreCase(uri.getScheme()))
{
content = uri;
uri = resolveContent(uri);
}
else
content = null;
if (BuildConfig.DEBUG)
Log.d(TAG, "Uri: " + uri);
// Read into new file if unresolved
if (CONTENT.equalsIgnoreCase(uri.getScheme()))
{
file = getNewFile();
Uri defaultUri = Uri.fromFile(file);
path = defaultUri.getPath();
setTitle(FileUtils.getDisplayName(this, content, null, null));
}
// Read file
else
{
this.uri = uri;
path = uri.getPath();
file = new File(path);
setTitle(uri.getLastPathSegment());
}
textView.setText(R.string.loading);
doRead(this, uri);
changed = false;
modified = file.lastModified();
savePath(path);
invalidateOptionsMenu();
}
// resolveContent
private Uri resolveContent(Uri uri)
{
String path = FileUtils.getPath(this, uri);
if (path != null)
{
File file = new File(path);
if (file.canRead())
uri = Uri.fromFile(file);
}
return uri;
}
// saveCheck
private void saveCheck()
{
Uri uri = Uri.fromFile(file);
Uri newUri = Uri.fromFile(getNewFile());
if (content == null && newUri.getPath().equals(uri.getPath()))
saveAs();
else
saveFile();
}
// saveFile
private void saveFile()
{
// Stop highlighting
textView.removeCallbacks(updateHighlight);
textView.removeCallbacks(updateWordCount);
if (file.lastModified() > modified)
alertDialog(this, R.string.appName, R.string.changedOverwrite,
R.string.overwrite, R.string.cancel, (dialog, id) ->
{
switch (id)
{
case DialogInterface.BUTTON_POSITIVE:
saveFile(file);
break;
}
});
else
{
if (content == null)
saveFile(file);
else
saveFile(content);
}
}
// saveFile
private void saveFile(File file)
{
CharSequence text = textView.getText();
write(text, file);
}
// saveFile
private void saveFile(Uri uri)
{
CharSequence text = textView.getText();
try (OutputStream outputStream =
getContentResolver().openOutputStream(uri, "rwt"))
{
write(text, outputStream);
}
catch (Exception e)
{
alertDialog(this, R.string.appName, e.getMessage(), R.string.ok);
e.printStackTrace();
return;
}
}
// write
private void write(CharSequence text, File file)
{
file.getParentFile().mkdirs();
String charset = UTF_8;
if (match != null && !match.equals(getString(R.string.detect)))
charset = match;
try (BufferedWriter writer = new BufferedWriter
(new OutputStreamWriter(new FileOutputStream(file), charset)))
{
writer.append(text);
writer.flush();
}
catch (Exception e)
{
alertDialog(this, R.string.appName, e.getMessage(), R.string.ok);
e.printStackTrace();
return;
}
changed = false;
invalidateOptionsMenu();
modified = file.lastModified();
savePath(file.getPath());
}
// write
private void write(CharSequence text, OutputStream os)
{
String charset = UTF_8;
if (match != null && !match.equals(getString(R.string.detect)))
charset = match;
try (BufferedWriter writer =
new BufferedWriter(new OutputStreamWriter(os, charset)))
{
writer.append(text);
writer.flush();
}
catch (Exception e)
{
alertDialog(this, R.string.appName, e.getMessage(), R.string.ok);
e.printStackTrace();
return;
}
changed = false;
invalidateOptionsMenu();
}
// checkHighlight
private void checkHighlight()
{
// No syntax
syntax = NO_SYNTAX;
// Check extension
if (highlight && file != null)
{
String ext = FileUtils.getExtension(file.getName());
if (ext != null)
{
String type = FileUtils.getMimeType(file);
if (ext.matches(CC_EXT))
syntax = CC_SYNTAX;
else if (ext.matches(HTML_EXT))
syntax = HTML_SYNTAX;
else if (ext.matches(CSS_EXT))
syntax = CSS_SYNTAX;
else if (ext.matches(ORG_EXT))
syntax = ORG_SYNTAX;
else if (ext.matches(MD_EXT))
syntax = MD_SYNTAX;
else if (ext.matches(SH_EXT))
syntax = SH_SYNTAX;
else if (!TEXT_PLAIN.equals(type))
syntax = DEF_SYNTAX;
else
syntax = NO_SYNTAX;
// Add callback
if (textView != null && syntax != NO_SYNTAX)
{
if (updateHighlight == null)
updateHighlight = () -> highlightText();
textView.removeCallbacks(updateHighlight);
textView.postDelayed(updateHighlight, UPDATE_DELAY);
return;
}
}
}
// Remove highlighting
if (updateHighlight != null)
{
textView.removeCallbacks(updateHighlight);
textView.postDelayed(updateHighlight, UPDATE_DELAY);
updateHighlight = null;
}
}
// highlightText
private void highlightText()
{
// Get visible extent
int top = scrollView.getScrollY();
int height = scrollView.getHeight();
int line = textView.getLayout().getLineForVertical(top);
int start = textView.getLayout().getLineStart(line);
int first = textView.getLayout().getLineStart(line + 1);
line = textView.getLayout().getLineForVertical(top + height);
int end = textView.getLayout().getLineEnd(line);
int last = (line == 0)? end:
textView.getLayout().getLineStart(line - 1);
// Move selection if outside range
if (textView.getSelectionStart() < start)
textView.setSelection(first);
if (textView.getSelectionStart() > end)
textView.setSelection(last);
// Get editable
Editable editable = textView.getEditableText();
// Get current spans
ForegroundColorSpan spans[] =
editable.getSpans(start, end, ForegroundColorSpan.class);
// Remove spans
for (ForegroundColorSpan span: spans)
editable.removeSpan(span);
Matcher matcher;
switch (syntax)
{
case NO_SYNTAX:
// Get current spans
spans = editable.getSpans(0, editable.length(),
ForegroundColorSpan.class);
// Remove spans
for (ForegroundColorSpan span: spans)
editable.removeSpan(span);
break;
case CC_SYNTAX:
matcher = KEYWORDS.matcher(editable);
matcher.region(start, end);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.CYAN);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(TYPES);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.MAGENTA);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(CLASS);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.BLUE);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(NUMBER);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.YELLOW);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(ANNOTATION);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.CYAN);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(CONSTANT);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.LTGRAY);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(OPERATOR);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.CYAN);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(CC_COMMENT);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.RED);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
break;
case HTML_SYNTAX:
matcher = HTML_TAGS.matcher(editable);
matcher.region(start, end);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.CYAN);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(HTML_ATTRS);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.MAGENTA);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(QUOTED);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.RED);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(HTML_COMMENT);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.RED);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
break;
case CSS_SYNTAX:
matcher = CSS_STYLES.matcher(editable);
matcher.region(start, end);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.CYAN);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(CSS_HEX);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.MAGENTA);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(CC_COMMENT);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.RED);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
break;
case ORG_SYNTAX:
matcher = ORG_HEADER.matcher(editable);
matcher.region(start, end);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.BLUE);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(ORG_EMPH);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.MAGENTA);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(ORG_LINK);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.CYAN);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(ORG_COMMENT);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.RED);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
break;
case MD_SYNTAX:
matcher = MD_HEADER.matcher(editable);
matcher.region(start, end);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.BLUE);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(MD_LINK);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.CYAN);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(MD_EMPH);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.MAGENTA);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(MD_CODE);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.CYAN);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
break;
case SH_SYNTAX:
matcher = KEYWORDS.matcher(editable);
matcher.region(start, end);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.CYAN);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(NUMBER);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.YELLOW);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(CONSTANT);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.LTGRAY);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(SH_VAR);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.MAGENTA);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(OPERATOR);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.CYAN);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(QUOTED);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.RED);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(SH_COMMENT);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.RED);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
break;
case DEF_SYNTAX:
matcher = KEYWORDS.matcher(editable);
matcher.region(start, end);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.CYAN);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(TYPES);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.MAGENTA);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(CLASS);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.BLUE);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(NUMBER);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.YELLOW);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(CONSTANT);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.LTGRAY);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.region(start, end).usePattern(QUOTED);
while (matcher.find())
{
ForegroundColorSpan span = new
ForegroundColorSpan(Color.RED);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
break;
}
}
// wordCountText
private void wordCountText()
{
int words = 0;
Matcher matcher = WORD_PATTERN.matcher(textView.getText());
while (matcher.find())
{
words++;
}
if (customView != null)
{
String string = String.format(Locale.getDefault(), "%d\n%d",
words, textView.length());
customView.setText(string);
}
}
// onActionModeStarted
@Override
public void onActionModeStarted(ActionMode mode)
{
super.onActionModeStarted(mode);
// If there's a file
if (file != null)
{
// Get the mime type
String type = FileUtils.getMimeType(file);
// If the type is not text/plain
if (!TEXT_PLAIN.equals(type))
{
// Get the start and end of the selection
int start = textView.getSelectionStart();
int end = textView.getSelectionEnd();
// And the text
CharSequence text = textView.getText();
// Get a pattern and a matcher for delimiter
// characters
Matcher matcher = PATTERN_CHARS.matcher(text);
// Find the first match after the end of the selection
if (matcher.find(end))
{
// Update the selection end
end = matcher.start();
// Get the matched char
char c = text.charAt(end);
// Check for opening brackets
if (BRACKET_CHARS.indexOf(c) == -1)
{
switch (c)
{
// Check for close brackets and look for
// the open brackets
case ')':
c = '(';
break;
case ']':
c = '[';
break;
case '}':
c = '{';
break;
case '>':
c = '<';
break;
}
String string = text.toString();
// Do reverse search
start = string.lastIndexOf(c, start) + 1;
// Check for included newline
if (start > string.lastIndexOf('\n', end))
// Update selection
textView.setSelection(start, end);
}
}
}
}
}
// checkMode
private void checkMode(CharSequence text)
{
boolean change = false;
CharSequence first = text.subSequence
(0, Math.min(text.length(), FIRST_SIZE));
CharSequence last = text.subSequence
(Math.max(0, text.length() - LAST_SIZE), text.length());
for (CharSequence sequence: new CharSequence[]{first, last})
{
Matcher matcher = MODE_PATTERN.matcher(sequence);
if (matcher.find())
{
matcher.region(matcher.start(1), matcher.end(1));
matcher.usePattern(OPTION_PATTERN);
while (matcher.find())
{
boolean no = "no".equals(matcher.group(2));
if ("vw".equals(matcher.group(3)))
{
if (view == no)
{
view = !no;
change = true;
}
}
else if ("ww".equals(matcher.group(3)))
{
if (wrap == no)
{
wrap = !no;
change = true;
}
}
else if ("sg".equals(matcher.group(3)))
{
if (suggest == no)
{
suggest = !no;
change = true;
}
}
else if ("hs".equals(matcher.group(3)))
{
if (highlight == no)
{
highlight = !no;
checkHighlight();
}
}
else if ("th".equals(matcher.group(3)))
{
if (":l".equals(matcher.group(4)))
{
if (theme != LIGHT)
{
theme = LIGHT;
change = true;
}
}
else if (":d".equals(matcher.group(4)))
{
if (theme != DARK)
{
theme = DARK;
change = true;
}
}
else if (":s".equals(matcher.group(4)))
{
if (theme != SYSTEM)
{
theme = SYSTEM;
change = true;
}
}
else if (":w".equals(matcher.group(4)))
{
if (theme != WHITE)
{
theme = WHITE;
change = true;
}
}
else if (":b".equals(matcher.group(4)))
{
if (theme != BLACK)
{
theme = BLACK;
change = true;
}
}
else if (":r".equals(matcher.group(4)))
{
if (theme != RETRO)
{
theme = RETRO;
change = true;
}
}
}
else if ("ts".equals(matcher.group(3)))
{
if (":l".equals(matcher.group(4)))
{
if (size != LARGE)
{
size = LARGE;
textView.setTextSize(size);
}
}
else if (":m".equals(matcher.group(4)))
{
if (size != MEDIUM)
{
size = MEDIUM;
textView.setTextSize(size);
}
}
else if (":s".equals(matcher.group(4)))
{
if (size != SMALL)
{
size = SMALL;
textView.setTextSize(size);
}
}
}
else if ("tf".equals(matcher.group(3)))
{
if (":m".equals(matcher.group(4)))
{
if (type != MONO)
{
type = MONO;
textView.setTypeface(Typeface.MONOSPACE);
}
}
else if (":p".equals(matcher.group(4)))
{
if (type != NORMAL)
{
type = NORMAL;
textView.setTypeface(Typeface.DEFAULT);
}
}
else if (":s".equals(matcher.group(4)))
{
if (type != SERIF)
{
type = SERIF;
textView.setTypeface(Typeface.SERIF);
}
}
}
else if ("cs".equals(matcher.group(3)))
{
if (":u".equals(matcher.group(4)))
{
match = UTF_8;
getActionBar().setSubtitle(match);
}
}
}
}
}
if (change)
recreate(this);
}
// loadText
private void loadText(CharSequence text)
{
if (textView != null)
textView.setText(text);
changed = false;
// Check for saved position
if (pathMap.containsKey(path))
textView.postDelayed(() ->
scrollView.smoothScrollTo
(0, pathMap.get(path)),
POSITION_DELAY);
else
textView.postDelayed(() ->
scrollView.smoothScrollTo(0, 0),
POSITION_DELAY);
// Check mode
checkMode(text);
// Check highlighting
checkHighlight();
// Set read only
if (view)
{
textView.setRawInputType(InputType.TYPE_NULL);
textView.setTextIsSelectable(true);
// Update boolean
edit = false;
}
else
{
// Set editable with or without suggestions
if (suggest)
textView.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_FLAG_MULTI_LINE);
else
textView.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_FLAG_MULTI_LINE |
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
// Update boolean
edit = true;
}
// Dismiss keyboard
textView.clearFocus();
// Update menu
invalidateOptionsMenu();
}
// QueryTextListener
private class QueryTextListener
implements SearchView.OnQueryTextListener
{
private BackgroundColorSpan span = new
BackgroundColorSpan(Color.YELLOW);
private Editable editable;
private Matcher matcher;
private Pattern pattern;
private int index;
private int height;
// onQueryTextChange
@Override
@SuppressWarnings("deprecation")
public boolean onQueryTextChange(String newText)
{
// Use regex search and spannable for highlighting
height = scrollView.getHeight();
editable = textView.getEditableText();
// Reset the index and clear highlighting
if (newText.length() == 0)
{
index = 0;
editable.removeSpan(span);
return false;
}
// Check pattern
try
{
pattern = Pattern.compile(newText, Pattern.MULTILINE);
matcher = pattern.matcher(editable);
}
catch (Exception e)
{
return false;
}
// Find text
if (matcher.find(index))
{
// Get index
index = matcher.start();
// Check layout
if (textView.getLayout() == null)
return false;
// Get text position
int line = textView.getLayout().getLineForOffset(index);
int pos = textView.getLayout().getLineBaseline(line);
// Scroll to it
scrollView.smoothScrollTo(0, pos - height / 2);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
else
index = 0;
return true;
}
// onQueryTextSubmit
@Override
public boolean onQueryTextSubmit(String query)
{
// Check matcher
if (matcher == null)
return false;
// Find next text
if (matcher.find())
{
// Get index
index = matcher.start();
// Get text position
int line = textView.getLayout().getLineForOffset(index);
int pos = textView.getLayout().getLineBaseline(line);
// Scroll to it
scrollView.smoothScrollTo(0, pos - height / 2);
// Highlight it
editable.setSpan(span, matcher.start(), matcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
else
{
matcher.reset();
index = 0;
}
return true;
}
}
// readFile
private CharSequence readFile(File file)
{
StringBuilder text = new StringBuilder();
// Open file
try (BufferedReader reader = new BufferedReader
(new InputStreamReader
(new BufferedInputStream(new FileInputStream(file)))))
{
String line;
while ((line = reader.readLine()) != null)
{
text.append(line);
text.append(System.getProperty("line.separator"));
}
return text;
}
catch (Exception e)
{
e.printStackTrace();
}
return text;
}
// ScaleListener
private class ScaleListener
extends ScaleGestureDetector.SimpleOnScaleGestureListener
{
// onScale
@Override
public boolean onScale(ScaleGestureDetector detector)
{
size *= Math.cbrt(detector.getScaleFactor());
size = Math.max(TINY, Math.min(size, HUGE));
textView.setTextSize(size);
invalidateOptionsMenu();
return true;
}
}
private void doFind(Context context, String search)
{
List matchList = new ArrayList<>();
List entries = new ArrayList<>();
// Get entry list
for (String path : pathMap.keySet())
{
File entry = new File(path);
entries.add(entry);
}
executor.execute(() ->
{
Pattern pattern = null;
try
{
// Compile search pattern
pattern = Pattern.compile(search, Pattern.MULTILINE);
}
catch (Exception e) {}
// Check the entries
for (File file : entries)
{
CharSequence content = readFile(file);
Matcher matcher = pattern.matcher(content);
if (matcher.find())
matchList.add(file);
}
runOnUiThread(() ->
{
// Build dialog
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.findAll);
// If found populate dialog
if (!matchList.isEmpty())
{
List choiceList = new ArrayList<>();
for (File file : matchList)
{
// Remove path prefix
String path = file.getPath();
String name =
path.replaceFirst(Environment
.getExternalStorageDirectory()
.getPath() + File.separator, "");
choiceList.add(name);
}
String[] choices = choiceList.toArray(new String[0]);
builder.setItems(choices, (dialog, which) ->
{
File file = matchList.get(which);
Uri uri = Uri.fromFile(file);
// Open the entry chosen
readFile(uri);
// Put the search text back - why it
// disappears I have no idea or why I have to
// do it after a delay
searchView.postDelayed(() ->
searchView.setQuery(search, false),
FIND_DELAY);
});
}
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
});
});
}
// doRead
private void doRead(Context context, Uri uri)
{
StringBuilder stringBuilder = new StringBuilder();
// Default UTF-8
if (match == null)
{
match = UTF_8;
getActionBar().setSubtitle(match);
}
executor.execute(() ->
{
try (BufferedInputStream in = new BufferedInputStream
(getContentResolver().openInputStream(uri)))
{
// Create reader
BufferedReader reader = null;
if (match.equals(getString(R.string.detect)))
{
// Detect charset, using UTF-8 hint
CharsetMatch charsetMatch = new
CharsetDetector().setDeclaredEncoding(UTF_8)
.setText(in).detect();
if (charsetMatch != null)
{
match = charsetMatch.getName();
runOnUiThread(() ->
getActionBar().setSubtitle(match));
reader = new BufferedReader(charsetMatch.getReader());
}
else
reader = new BufferedReader
(new InputStreamReader(in));
if (BuildConfig.DEBUG && match != null)
Log.d(TAG, "Charset " + match);
}
else
reader = new BufferedReader
(new InputStreamReader(in, match));
String line;
while ((line = reader.readLine()) != null)
{
stringBuilder.append(line);
stringBuilder.append(System.getProperty("line.separator"));
}
}
catch (Exception e)
{
runOnUiThread(() ->
alertDialog(context, R.string.appName,
e.getMessage(),
R.string.ok));
e.printStackTrace();
}
runOnUiThread(() -> loadText(stringBuilder));
});
}
}
================================================
FILE: src/main/java/org/billthefarmer/editor/FileAdapter.java
================================================
////////////////////////////////////////////////////////////////////////////////
//
// Editor - Text editor for Android
//
// Copyright © 2017 Bill Farmer
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
////////////////////////////////////////////////////////////////////////////////
package org.billthefarmer.editor;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Environment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.io.File;
import java.util.List;
// FileAdapter class
public class FileAdapter extends BaseAdapter
{
private final static String TAG = "FileAdapter";
private final static String ROOT = "/";
private final static String IMAGE_SVG = "image/svg";
private final static String IMAGE = "image";
private final static String AUDIO = "audio";
private final static String VIDEO = "video";
private final static String APPLICATION = "application";
private final static long TOO_LARGE = 524288;
private LayoutInflater inflater;
private List files;
private int fileId;
private int audioId;
private int imageId;
private int videoId;
private int folderId;
private int parentId;
private int externalId;
private int applicationId;
// Constructor
public FileAdapter(Context context, List files)
{
inflater = LayoutInflater.from(context);
this.files = files;
final TypedArray typedArray =
context.obtainStyledAttributes(R.styleable.Editor);
if (typedArray.hasValue(R.styleable.Editor_folder))
folderId =
typedArray.getResourceId(R.styleable.Editor_folder, 0);
if (typedArray.hasValue(R.styleable.Editor_parent))
parentId =
typedArray.getResourceId(R.styleable.Editor_parent, 0);
if (typedArray.hasValue(R.styleable.Editor_file))
fileId =
typedArray.getResourceId(R.styleable.Editor_file, 0);
if (typedArray.hasValue(R.styleable.Editor_audio))
audioId =
typedArray.getResourceId(R.styleable.Editor_audio, 0);
if (typedArray.hasValue(R.styleable.Editor_image))
imageId =
typedArray.getResourceId(R.styleable.Editor_image, 0);
if (typedArray.hasValue(R.styleable.Editor_video))
videoId =
typedArray.getResourceId(R.styleable.Editor_video, 0);
if (typedArray.hasValue(R.styleable.Editor_external))
externalId =
typedArray.getResourceId(R.styleable.Editor_external, 0);
if (typedArray.hasValue(R.styleable.Editor_application))
applicationId =
typedArray.getResourceId(R.styleable.Editor_application, 0);
typedArray.recycle();
}
@Override
public int getCount()
{
return files.size();
}
@Override
public Object getItem(int position)
{
return null;
}
@Override
public long getItemId(int position)
{
return position;
}
// Create a new View for each item referenced by the adapter
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
// Create a new view
if (convertView == null)
convertView = inflater.inflate(R.layout.file, parent, false);
// Find the views
TextView name = convertView.findViewById(R.id.name);
File file = files.get(position);
if (name != null)
{
if (file.getParentFile() == null)
name.setText(ROOT);
else
name.setText(file.getName());
name.setEnabled(true);
name.setClickable(false);
if (file.isDirectory())
{
if (position == 0)
name.setCompoundDrawablesWithIntrinsicBounds(parentId,
0, 0, 0);
else
name.setCompoundDrawablesWithIntrinsicBounds(folderId,
0, 0, 0);
}
else
{
// Get the mime type
String type = FileUtils.getMimeType(file);
if (type != null && type.startsWith(IMAGE_SVG))
name.setCompoundDrawablesWithIntrinsicBounds(imageId,
0, 0, 0);
else if (type != null && type.startsWith(IMAGE))
{
name.setEnabled(false);
name.setClickable(true);
name.setCompoundDrawablesWithIntrinsicBounds(imageId,
0, 0, 0);
}
else if (type != null && type.startsWith(AUDIO))
{
name.setEnabled(false);
name.setClickable(true);
name.setCompoundDrawablesWithIntrinsicBounds(audioId,
0, 0, 0);
}
else if (type != null && type.startsWith(VIDEO))
{
name.setEnabled(false);
name.setClickable(true);
name.setCompoundDrawablesWithIntrinsicBounds(videoId,
0, 0, 0);
}
else if (type != null && type.startsWith(APPLICATION))
name.setCompoundDrawablesWithIntrinsicBounds(applicationId,
0, 0, 0);
else
name.setCompoundDrawablesWithIntrinsicBounds(fileId,
0, 0, 0);
}
// Too large
if (file.length() > TOO_LARGE)
{
name.setEnabled(false);
name.setClickable(true);
}
}
return convertView;
}
}
================================================
FILE: src/main/java/org/billthefarmer/editor/FileUtils.java
================================================
/*
* Copyright (C) 2018 OpenIntents.org
*
* 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.billthefarmer.editor;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.util.Log;
import android.webkit.MimeTypeMap;
import java.io.File;
import java.io.FileFilter;
import java.text.DecimalFormat;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
/**
* @author Peli
* @author paulburke (ipaulpro)
* @author Bill Farmer (billthefarmer)
* @version 2017-06-22
*/
public class FileUtils
{
/**
* TAG for log messages.
*/
private static final String TAG = "FileUtils";
// pattern
/**
* File and folder comparator. TODO Expose sorting option method
*
* @author paulburke
*/
public static Comparator sComparator = (f1, f2) ->
{
// Sort alphabetically by lower case, which is much cleaner
return f1.getName().toLowerCase(Locale.getDefault())
.compareTo(f2.getName().toLowerCase(Locale.getDefault()));
};
public static final String MIME_TYPE_AUDIO = "audio/*";
public static final String MIME_TYPE_TEXT = "text/*";
public static final String MIME_TYPE_IMAGE = "image/*";
public static final String MIME_TYPE_VIDEO = "video/*";
public static final String MIME_TYPE_APP = "application/*";
public static final String HIDDEN_PREFIX = ".";
/**
* File (not directories) filter.
*
* @author paulburke
*/
public static FileFilter sFileFilter = file ->
{
final String fileName = file.getName();
// Return files only (not directories) and skip hidden files
return file.isFile() && !fileName.startsWith(HIDDEN_PREFIX);
};
/**
* Folder (directories) filter.
*
* @author paulburke
*/
public static FileFilter sDirFilter = file ->
{
final String fileName = file.getName();
// Return directories only and skip hidden directories
return file.isDirectory() && !fileName.startsWith(HIDDEN_PREFIX);
};
private FileUtils()
{
} // private constructor to enforce Singleton
/**
* Gets the extension of a file name, like ".png" or ".jpg".
*
* @param uri
* @return Extension including the dot("."); "" if there is no
* extension; null if uri was null.
*/
public static String getExtension(String uri)
{
if (uri == null)
{
return null;
}
int dot = uri.lastIndexOf(".");
if (dot >= 0)
{
return uri.substring(dot);
}
else
{
// No extension.
return "";
}
}
/**
* @return Whether the URI is a local one.
*/
public static boolean isLocal(String url)
{
return url != null && !url.startsWith("http://") &&
!url.startsWith("https://");
}
/**
* @return True if Uri is a MediaStore Uri.
* @author paulburke
*/
public static boolean isMediaUri(Uri uri)
{
return "media".equalsIgnoreCase(uri.getAuthority());
}
/**
* Convert File into Uri.
*
* @param file
* @return uri
*/
public static Uri getUri(File file)
{
return (file != null) ? Uri.fromFile(file) : null;
}
/**
* Returns the path only (without file name).
*
* @param file
* @return
*/
public static File getPathWithoutFilename(File file)
{
if (file != null)
{
if (file.isDirectory())
{
// no file to be split off. Return everything
return file;
}
else
{
String filename = file.getName();
String filepath = file.getAbsolutePath();
// Construct path without file name.
String pathwithoutname = filepath.substring(0,
filepath.length() - filename.length());
if (pathwithoutname.endsWith(File.separator))
{
pathwithoutname = pathwithoutname
.substring(0, pathwithoutname.length() - 1);
}
return new File(pathwithoutname);
}
}
return null;
}
/**
* @return The MIME type for the given file.
*/
public static String getMimeType(File file)
{
String extension =
getExtension(file.getName()).toLowerCase(Locale.getDefault());
if (extension.length() > 0)
return MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(extension.substring(1));
return "application/octet-stream";
}
/**
* @return The MIME type for the give Uri.
*/
public static String getMimeType(Context context, Uri uri)
{
File file = new File(getPath(context, uri));
return getMimeType(file);
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
* @author paulburke
*/
public static boolean isExternalStorageDocument(Uri uri)
{
return "com.android.externalstorage.documents"
.equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
* @author paulburke
*/
public static boolean isDownloadsDocument(Uri uri)
{
return "com.android.providers.downloads.documents"
.equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
* @author paulburke
*/
public static boolean isMediaDocument(Uri uri)
{
return "com.android.providers.media.documents"
.equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri)
{
return "com.google.android.apps.photos.content"
.equals(uri.getAuthority());
}
/**
* @param uri The Uri to match.
* @return The file path from the FileProvider Uri.
* @author billthefarmer
*/
public static String fileProviderPath(Uri uri)
{
List uriList = uri.getPathSegments();
if (BuildConfig.DEBUG)
{
Log.d(TAG, "Uri: " + uri);
Log.d(TAG, "Path: " + uriList);
}
List segments =
uriList.subList(1, uriList.size());
// Try root path
if (uriList.size() > 3)
{
StringBuilder path = new StringBuilder();
for (String segment : segments)
{
path.append(File.separator);
path.append(segment);
}
if (BuildConfig.DEBUG)
Log.d(TAG, "Path: " + path);
File file = new File(path.toString());
if (file.isFile())
return path.toString();
}
// Try external storage path
if (uriList.size() > 1)
{
StringBuilder path = new StringBuilder();
path.append(Environment.getExternalStorageDirectory());
for (String segment : segments)
{
path.append(File.separator);
path.append(segment);
}
if (BuildConfig.DEBUG)
Log.d(TAG, "Path: " + path);
File file = new File(path.toString());
if (file.isFile())
return path.toString();
}
return null;
}
/**
* Get the display name for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the
* query.
* @return The display name of the file referred to by the Uri
* @author Bill Farmer
*/
public static String getDisplayName(Context context, Uri uri,
String selection,
String[] selectionArgs)
{
final String column = OpenableColumns.DISPLAY_NAME;
final String[] projection =
{
column
};
try (Cursor cursor = context.getContentResolver()
.query(uri, projection, selection, selectionArgs, null))
{
if (cursor != null && cursor.moveToFirst())
{
if (BuildConfig.DEBUG)
DatabaseUtils.dumpCursor(cursor);
final int column_index = cursor.getColumnIndex(column);
if (column_index >= 0)
return cursor.getString(column_index);
}
}
catch (Exception e) {}
return null;
}
/**
* Get the size for this Uri. This is useful for MediaStore Uris,
* and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the
* query.
* @return The size of the file referred to by the Uri
* @author Bill Farmer
*/
public static long getSize(Context context, Uri uri,
String selection,
String[] selectionArgs)
{
final String column = OpenableColumns.SIZE;
final String[] projection =
{
column
};
try (Cursor cursor = context.getContentResolver()
.query(uri, projection, selection, selectionArgs, null))
{
if (cursor != null && cursor.moveToFirst())
{
if (BuildConfig.DEBUG)
DatabaseUtils.dumpCursor(cursor);
final int column_index = cursor.getColumnIndex(column);
if (column_index >= 0)
return cursor.getLong(column_index);
}
}
catch (Exception e) {}
return 0;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the
* query.
* @return The value of the _data column, which is typically a
* file path.
* @author paulburke
*/
public static String getDataColumn(Context context, Uri uri,
String selection,
String[] selectionArgs)
{
final String column = MediaStore.MediaColumns.DATA;
final String[] projection =
{
column
};
try (Cursor cursor = context.getContentResolver()
.query(uri, projection, selection, selectionArgs, null))
{
if (cursor != null && cursor.moveToFirst())
{
if (BuildConfig.DEBUG)
DatabaseUtils.dumpCursor(cursor);
final int column_index = cursor.getColumnIndex(column);
if (column_index >= 0)
return cursor.getString(column_index);
}
}
catch (Exception e) {}
return null;
}
/**
* Get a file path from a Uri. This will get the the path for
* Storage Access Framework Documents, as well as the _data field
* for the MediaStore and other file-based ContentProviders.
*
* Callers should check whether the path is local before assuming
* it represents a local file.
*
* @param context The context.
* @param uri The Uri to query.
* @author paulburke
* @see #isLocal(String)
* @see #getFile(Context, Uri)
*/
public static String getPath(final Context context, final Uri uri)
{
if (BuildConfig.DEBUG)
Log.d(TAG, "File: " +
"Authority: " + uri.getAuthority() +
", Fragment: " + uri.getFragment() +
", Port: " + uri.getPort() +
", Query: " + uri.getQuery() +
", Scheme: " + uri.getScheme() +
", Host: " + uri.getHost() +
", Segments: " + uri.getPathSegments().toString()
);
// DocumentProvider
if (DocumentsContract.isDocumentUri(context, uri))
{
// ExternalStorageProvider
if (isExternalStorageDocument(uri))
{
final List segments = uri.getPathSegments();
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
final String id = split[1];
if (BuildConfig.DEBUG)
Log.d(TAG, "Segments: " + segments);
if (BuildConfig.DEBUG)
Log.d(TAG, "DocId: " + docId);
if ("primary".equalsIgnoreCase(type))
{
return Environment.getExternalStorageDirectory() +
File.separator + id;
}
else if ("home".equalsIgnoreCase(type))
{
return Environment .getExternalStorageDirectory() +
"/Documents/" + id;
}
else if (type != null && type.matches("[0-9A-Z]{4}-[0-9A-Z]{4}"))
{
List storage =
Uri.fromFile(Environment.getExternalStorageDirectory())
.getPathSegments();
return File.separator + storage.get(0) + File.separator +
type + File.separator + id;
}
else if ("document".equalsIgnoreCase(segments.get(0)))
{
return Environment .getExternalStorageDirectory() +
File.separator + id;
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri))
{
// Check for non-numeric id
try
{
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri =
ContentUris
.withAppendedId(Uri.parse("content://downloads/public_downloads"),
Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// Id not a number
catch (Exception e)
{
return getDataColumn(context, uri, null, null);
}
}
// MediaProvider
else if (isMediaDocument(uri))
{
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
final String id = split[1];
Uri contentUri = null;
if ("image".equals(type))
{
contentUri =
MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
else if ("video".equals(type))
{
contentUri =
MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
}
else if ("audio".equals(type))
{
contentUri =
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]
{
id
};
return getDataColumn(context, contentUri,
selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme()))
{
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
// Return ContentProvider path
String path = getDataColumn(context, uri, null, null);
if (path != null)
return path;
// Return FileProvider path
return fileProviderPath(uri);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme()))
{
return uri.getPath();
}
return null;
}
/**
* Convert Uri into File, if possible.
*
* @return file A local file that the Uri was pointing to, or null
* if the Uri is unsupported or pointed to a remote
* resource.
* @author paulburke
* @see #getPath(Context, Uri)
*/
public static File getFile(Context context, Uri uri)
{
if (uri != null)
{
String path = getPath(context, uri);
if (isLocal(path))
{
return new File(path);
}
}
return null;
}
/**
* Get the file size in a human-readable string.
*
* @param size
* @return
* @author paulburke
*/
public static String getReadableFileSize(long size)
{
final int BYTES_IN_KILOBYTES = 1024;
final DecimalFormat dec = new DecimalFormat("###.#");
final String KILOBYTES = " KB";
final String MEGABYTES = " MB";
final String GIGABYTES = " GB";
float fileSize = 0;
String suffix = KILOBYTES;
if (size > BYTES_IN_KILOBYTES)
{
fileSize = size / BYTES_IN_KILOBYTES;
if (fileSize > BYTES_IN_KILOBYTES)
{
fileSize = fileSize / BYTES_IN_KILOBYTES;
if (fileSize > BYTES_IN_KILOBYTES)
{
fileSize = fileSize / BYTES_IN_KILOBYTES;
suffix = GIGABYTES;
}
else
{
suffix = MEGABYTES;
}
}
}
return String.valueOf(dec.format(fileSize) + suffix);
}
/**
* Attempt to retrieve the thumbnail of given File from the
* MediaStore. This should not be called on the UI thread.
*
* @param context
* @param file
* @return
* @author paulburke
*/
public static Bitmap getThumbnail(Context context, File file)
{
return getThumbnail(context, getUri(file), getMimeType(file));
}
/**
* Attempt to retrieve the thumbnail of given Uri from the
* MediaStore. This should not be called on the UI thread.
*
* @param context
* @param uri
* @return
* @author paulburke
*/
public static Bitmap getThumbnail(Context context, Uri uri)
{
return getThumbnail(context, uri, getMimeType(context, uri));
}
/**
* Attempt to retrieve the thumbnail of given Uri from the
* MediaStore. This should not be called on the UI thread.
*
* @param context
* @param uri
* @param mimeType
* @return
* @author paulburke
*/
@SuppressWarnings("deprecation")
public static Bitmap getThumbnail(Context context, Uri uri,
String mimeType)
{
if (BuildConfig.DEBUG)
Log.d(TAG, "Attempting to get thumbnail");
if (!isMediaUri(uri))
{
Log.e(TAG,
"You can only retrieve thumbnails for images and videos.");
return null;
}
Bitmap bm = null;
final ContentResolver resolver = context.getContentResolver();
try (Cursor cursor = resolver.query(uri, null, null, null, null))
{
if (cursor.moveToFirst())
{
final int id = cursor.getInt(0);
if (BuildConfig.DEBUG)
Log.d(TAG, "Got thumb ID: " + id);
if (mimeType.contains("video"))
{
bm = MediaStore.Video.Thumbnails.getThumbnail(
resolver,
id,
MediaStore.Video.Thumbnails.MINI_KIND,
null);
}
else if (mimeType.contains(FileUtils.MIME_TYPE_IMAGE))
{
bm = MediaStore.Images.Thumbnails.getThumbnail(
resolver,
id,
MediaStore.Images.Thumbnails.MINI_KIND,
null);
}
}
}
catch (Exception e)
{
if (BuildConfig.DEBUG)
Log.e(TAG, "getThumbnail", e);
}
return bm;
}
/**
* Get the Intent for selecting content to be used in an Intent Chooser.
*
* @return The intent for opening a file with Intent.createChooser()
* @author paulburke
*/
public static Intent createGetContentIntent()
{
// Implicitly allow the user to select a particular kind of data
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
// The MIME data type filter
intent.setType("*/*");
// Only return URIs that can be opened with ContentResolver
intent.addCategory(Intent.CATEGORY_OPENABLE);
return intent;
}
}
================================================
FILE: src/main/java/org/billthefarmer/editor/NewFile.java
================================================
////////////////////////////////////////////////////////////////////////////////
//
// Editor - Text editor for Android
//
// Copyright © 2019 Bill Farmer
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
// Bill Farmer william j farmer [at] yahoo [dot] co [dot] uk.
//
////////////////////////////////////////////////////////////////////////////////
package org.billthefarmer.editor;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class NewFile extends Activity
{
public final static String TAG = "NewFile";
// onCreate
@Override
@SuppressWarnings("deprecation")
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Create the shortcut intent
Intent shortcut = new
Intent(this, Editor.class);
shortcut.setAction(Editor.OPEN_NEW);
shortcut.addCategory(Intent.CATEGORY_DEFAULT);
// Create the shortcut
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcut);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME,
getString(R.string.newFile));
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext
(this, R.drawable.ic_launcher));
setResult(RESULT_OK, intent);
finish();
}
}
================================================
FILE: src/main/java/org/billthefarmer/editor/OpenFile.java
================================================
////////////////////////////////////////////////////////////////////////////////
//
// Editor - Text editor for Android
//
// Copyright © 2019 Bill Farmer
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
// Bill Farmer william j farmer [at] yahoo [dot] co [dot] uk.
//
////////////////////////////////////////////////////////////////////////////////
package org.billthefarmer.editor;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class OpenFile extends Activity
{
public final static String TAG = "OpenFile";
private TextView nameView;
private TextView pathView;
private String path;
private File file;
private Uri uri;
// onCreate
@Override
@SuppressWarnings("deprecation")
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Get preferences
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(this);
int theme = preferences.getInt(Editor.PREF_THEME, Editor.LIGHT);
// Get day/night mode
Configuration config = getResources().getConfiguration();
int night = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set theme
switch (theme)
{
case Editor.LIGHT:
setTheme(R.style.DialogTheme);
break;
case Editor.DARK:
setTheme(R.style.DialogDarkTheme);
break;
case Editor.SYSTEM:
switch (night)
{
case Configuration.UI_MODE_NIGHT_NO:
setTheme(R.style.DialogTheme);
break;
case Configuration.UI_MODE_NIGHT_YES:
setTheme(R.style.DialogDarkTheme);
break;
}
break;
case Editor.WHITE:
setTheme(R.style.DialogWhiteTheme);
break;
case Editor.BLACK:
setTheme(R.style.DialogBlackTheme);
break;
case Editor.RETRO:
setTheme(R.style.DialogRetroTheme);
break;
}
// Set content
setContentView(R.layout.open_file);
// Find views
nameView = findViewById(R.id.name);
pathView = findViewById(R.id.path);
// Get last path
boolean last = preferences.getBoolean(Editor.PREF_LAST, false);
String lastPath = preferences.getString(Editor.PREF_FILE, "");
// Configure buttons
Button cancel = findViewById(R.id.cancel);
cancel.setOnClickListener((v) ->
{
setResult(RESULT_CANCELED, null);
finish();
});
Button openFile = findViewById(R.id.openFile);
openFile.setOnClickListener((v) ->
{
if (last && !lastPath.isEmpty())
getFile(new File(lastPath).getParentFile());
else
getFile(new File(Environment.getExternalStorageDirectory(),
Editor.DOCUMENTS));
});
Button create = findViewById(R.id.create);
create.setOnClickListener((v) ->
{
if (pathView.length() == 0)
return;
// Create the shortcut intent
Intent shortcut = new Intent(this, Editor.class);
shortcut.setAction(Intent.ACTION_EDIT);
shortcut.addCategory(Intent.CATEGORY_DEFAULT);
if (uri == null)
{
file = new File(pathView.getText().toString());
if (!file.isAbsolute())
file = new File(Environment.getExternalStorageDirectory(),
file.getPath());
uri = Uri.fromFile(file);
if (nameView.length() == 0)
nameView.setText(uri.getLastPathSegment());
}
// Set uri
shortcut.setData(uri);
// Create the shortcut
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcut);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME,
nameView.getText().toString());
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext
(this, R.drawable.ic_launcher));
setResult(RESULT_OK, intent);
finish();
});
// Check permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED)
{
requestPermissions(new String[]
{Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE},
Editor.REQUEST_OPEN);
return;
}
}
if (last && !lastPath.isEmpty())
getFile(new File(lastPath).getParentFile());
else
getFile(new File(Environment.getExternalStorageDirectory(),
Editor.DOCUMENTS));
}
// onRequestPermissionsResult
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions,
int[] grantResults)
{
switch (requestCode)
{
case Editor.REQUEST_OPEN:
for (int i = 0; i < grantResults.length; i++)
if (permissions[i].equals(Manifest.permission
.READ_EXTERNAL_STORAGE) &&
grantResults[i] == PackageManager.PERMISSION_GRANTED)
// Granted, get file
getFile(new File(Environment.getExternalStorageDirectory(),
Editor.DOCUMENTS));
break;
}
}
// onActivityResult
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data)
{
if (resultCode == RESULT_CANCELED)
return;
switch (requestCode)
{
case Editor.OPEN_DOCUMENT:
uri = data.getData();
path = uri.getPath();
pathView.setText(path);
nameView.setText(FileUtils.getDisplayName(this, uri,
null, null));
break;
}
}
// getFile
private void getFile(File dir)
{
// Get list of files
List fileList = Editor.getList(dir);
if (fileList == null)
return;
// Get list of folders
List dirList = new ArrayList();
dirList.add(File.separator);
dirList.addAll(Uri.fromFile(dir).getPathSegments());
// Pop up dialog
Editor.openDialog(this, dirList, fileList, (dialog, which) ->
{
if (DialogInterface.BUTTON_NEUTRAL == which)
{
// Use storage
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType(Editor.TEXT_WILD);
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, Editor.OPEN_DOCUMENT);
return;
}
if (Editor.FOLDER_OFFSET <= which)
{
File file = new File(File.separator);
for (int i = 0; i <= which - Editor.FOLDER_OFFSET; i++)
file = new File(file, dirList.get(i));
if (file.isDirectory())
getFile(file);
return;
}
File selection = fileList.get(which);
if (selection.isDirectory())
getFile(selection);
else
{
uri = Uri.fromFile(selection);
path = uri.getPath();
pathView.setText(path);
nameView.setText(uri.getLastPathSegment());
}
});
}
}
================================================
FILE: src/main/res/color/text_color_retro.xml
================================================
================================================
FILE: src/main/res/drawable/ic_add_black_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_add_white_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_edit_black_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_edit_white_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_folder_open_black_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_folder_open_white_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_image_black_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_image_white_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_list_black_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_list_white_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_reply_black_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_reply_white_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_save_black_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_save_white_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_sd_storage_black_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_sd_storage_white_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_stop_black_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_stop_white_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_videocam_black_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_videocam_white_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_volume_up_black_24dp.xml
================================================
================================================
FILE: src/main/res/drawable/ic_volume_up_white_24dp.xml
================================================
================================================
FILE: src/main/res/layout/custom.xml
================================================
================================================
FILE: src/main/res/layout/edit.xml
================================================
================================================
FILE: src/main/res/layout/file.xml
================================================
================================================
FILE: src/main/res/layout/open_file.xml
================================================
================================================
FILE: src/main/res/layout/save_path.xml
================================================
================================================
FILE: src/main/res/layout/seek_bar.xml
================================================
================================================
FILE: src/main/res/layout/wrap.xml
================================================
================================================
FILE: src/main/res/menu/main.xml
================================================
-
-
-
-
-
-
================================================
FILE: src/main/res/values/arrays.xml
================================================
- arial
- baskerville
- casual
- courier new
- courier
- cursive
- fantasy
- georgia
- goudy
- helvetica
- monaco
- monospace
- palatino
- sans-serif-black
- sans-serif-condensed-light
- sans-serif-condensed-medium
- sans-serif-condensed
- sans-serif-light
- sans-serif-medium
- sans-serif-monospace
- sans-serif-smallcaps
- sans-serif-thin
- sans-serif
- serif-monospace
- serif
- source-sans-pro-semi-bold
- source-sans-pro
- tahoma
- times new roman
- times
- verdana
================================================
FILE: src/main/res/values/attrs.xml
================================================
================================================
FILE: src/main/res/values/colours.xml
================================================
#ff00ff00
#4c00ff00
================================================
FILE: src/main/res/values/strings.xml
================================================
Editor
New file
Open file
Name
Path
Create
Edit
View
Open file…
Save
Storage
Open recent
Clear list
Search…
Find all…
Save as…
Detect
Go to…
Print…
View markdown…
Options
View files
Open last
Auto save
Word wrap
Suggestions
Highlight syntax
Theme
Light
Dark
System
White
Black
Retro
Text size
Small
Medium
Large
Typeface
Monospace
Proportional
Sans serif
Serif
About
%s \n\nBuilt %s\n\nCopyright \u00A9 2017 Bill Farmer \n\nArabic translation by MuhammedSaleem brej ,French translation by Étienne Deparis , German translation by Markus Weimar and Alexander Ritter , Italian translation by Michael Moroni , Polish translation by Evo , Greek translation by nikoss , Spanish translation by Miguel Caceres , Simplified Chinese translation by Lu Chang (ludoux) , Dutch translation by Heimen Stoffels (vistaus) , Russian translation by Igor Nedoboy , Latvian translation by Rihards Skuja , Icelandic translation by Sveinn í Felli , Portuguese translation by André Glória , Chinese (Taiwan) translation by cges30901 , Turkish translation by Yaşar Çiv , Brazilian Portuguese translation by aevw , Sorani Kurdish translation by Rawand Saeed , Persian (Farsi) translation by Eshagh , Hungarian translation by Balázs Úr , Japanese translation by dokutoku , Belarusian translation by Makar Razin , Basque (eu) translation by alexgabi \n\nLicence GNU GPLv3
Too large %s
Choose a file name
Loading…
You have unsaved changes. Do you want to save your changes?
This file has changed, do you want to reload it?
This file has changed, do you want to overwrite it?
In order to have access to the Documents and other folders Editor must use the Files or All files access permission depending upon the version of Android. If this is not allowed the app may still be able to access files using the Storage button in the file chooser.
Files
Access all files
OK
Reload
Overwrite
Discard
Cancel
================================================
FILE: src/main/res/values/styles.xml
================================================
================================================
FILE: src/main/res/values-ar/strings.xml
================================================
Editor
ملف جديد
فتح ملف
الاسم
المسار
إنشاء
تعديل
عرض
فتح ملف…
حفظ
الذاكرة
المفتوحة مؤخرا
تنظيف القائمة
بحث…
إيجاد الكل…
الحفظ ك…
اكتشاف
الذهاب إلى…
طباعة…
عرض markdown…
الخيارات
عرض الملفات
الفتح على الأخير
الحفظ التلقاءي
تقسيم الأسطر الطويلة
الاقتراحات
تمييز بناء الجملة
السمة
مضيء
مظلم
النظام
أبيض
أسود
قديم
حجم النص
صغير
متوسط
ضخم
نوع الخط
Monospace
Proportional
Sans serif
Serif
حول
%s \n\nBuilt %s\n\nحقوق طبع \u00A9 2017 Bill Farmer \n\nArabic translation by MuhammedSaleem brej ,French translation by Étienne Deparis , German translation by Markus Weimar and Alexander Ritter , Italian translation by Michael Moroni , Polish translation by Evo , Greek translation by nikoss , Spanish translation by Miguel Caceres , Simplified Chinese translation by Lu Chang (ludoux) , Dutch translation by Heimen Stoffels (vistaus) , Russian translation by Igor Nedoboy , Latvian translation by Rihards Skuja , Icelandic translation by Sveinn í Felli , Portuguese translation by André Glória , Chinese (Taiwan) translation by cges30901 , Turkish translation by Yaşar Çiv , Brazilian Portuguese translation by aevw , Sorani Kurdish translation by Rawand Saeed , Persian (Farsi) translation by Eshagh , Hungarian translation by Balázs Úr , Japanese translation by dokutoku , Belarusian translation by Makar Razin , Basque (eu) translation by alexgabi \n\nالرخصة GNU GPLv3
ضخم جدا %s
تتم المعالجة…
هناك تعديلات غير محفوظة، هل تريد حفظها؟
لقد تم تعديل هذا الملف، هل تريد إعادة تحميله؟
لقد تم تعديل هذا الملف، هل تريد الكتابة فوقه؟
حسنا
إعادة التحميل
الكتابة فوق
تجاهل
إلغاء
================================================
FILE: src/main/res/values-be/strings.xml
================================================
Рэдактар
Пошук…
Адкрыць файл
Імя
Шлях
Ствараць
Знайсці ўсё…
Новы
Рэдактар
Прагляд
Адкрыць файл…
Захаваць
Сховішча
Нядаўнія
Ачысціць спіс
Захаваць як…
Выяўляць
Перайсці да…
Друкаваць…
Прагляд Markdown…
Параметры
Прагляд файлаў
Адкрыты апошнім
Аўтазахаванне
Перанос радкоў
Варыянты слоў
Вылучыць сінтаксіс
Тэма
Светлая
Цёмная
Белая
Чорная
Сістэмная
Рэтра
Памер шрыфта
Малы
Сярэдні
Буйны
Шрыфт
Манашырынны
Прапарцыйны
Без засечак
З засечкамі
Аб праграме
%s \n\nЗборка
%s\nРаспрацоўка \u00A9 2017 Bill Farmer\n\nБеларускі пераклад \u00A9 Makar Razin \n\nЛіцэнзія GNU GPLv3
Занадта вялікі %s
Выберыце імя файла
Чытанне…
Захаваць змены?
Файл змяніўся, перачытаць?
Файл змяніўся, перазапісаць?
ОК
Перачытаць
Перазапісаць
Не захоўваць
Адмена
================================================
FILE: src/main/res/values-de/strings.xml
================================================
Editor
Neu
Datei öffnen
Name
Pfad
Erstellen
Bearbeiten
Betrachten
Datei öffnen…
Speichern
Speicher
Zuletzt geöffnet
Liste leeren
Suchen…
Alle finden…
Speichern unter…
Erkennen
Gehe zu…
Drucken…
Markdown betrachten…
Optionen
Dateien anzeigen
Zuletzt geöffnet
Automatisch speichern
Zeilenumbruch
Vorschläge
Syntax hervorheben
Thema
Hell
Dunkel
System
Weiß
Schwarz
Retro
Schriftgröße
Klein
Mittel
Groß
Schriftart
Nichtproportional
Proportional
Serifenlos
Serife
Über
%s \n\nBuild
%s\n\nCopyright \u00A9 2017 Bill Farmer\n\nDeutsche Übersetzung von
Markus Weimar und
Alexander Ritter \n\nLizenz
GNU GPLv3
Zu groß %s
Geben Sie einen Dateinamen ein.
Laden…
Ihre Änderungen wurden noch nicht gespeichert. Möchten Sie diese speichern?
Die Datei wurde geändert. Möchten Sie diese neu laden?
Die Datei wurde geändert. Möchten Sie diese überschreiben?
OK
Neu laden
Überschreiben
Verwerfen
Abbrechen
================================================
FILE: src/main/res/values-el/strings.xml
================================================
Επεξεργαστής
Νέος
Άνοιγμα αρχείου
Όνομα
Μονοπάτι
Δημιουργώ
Επεξεργασία
Προβολή
Άνοιγμα αρχείου…
Αποθήκευση
Αποθηκευτικός χώρος
Άνοιγμα πρόσφατου
Σαφή κατάλογο
Αναζήτηση…
Βρείτε όλα…
Αποθήκευση ως…
Ανιχνεύουν
Πήγαινε στο…
Τυπώνω…
Προβολή της Markdown…
Επιλογές
Προβολή αρχείων
Ανοιχτό τελευταίο
Αυτόματη αποθήκευση
Αναδίπλωση λέξεων
Προτάσεις
Επισήμανση σύνταξης
Θέμα
Φωτεινό
Άσπρος
Σκοτεινό
Μαύρος
Σύστημα
Παλιότροπο
Μέγεθος κειμένου
Μικρό
Μεσαίο
Μεγάλο
Γραμματοσειρά
Σταθερού πλάτους
Αναλογική
Χωρίς σερίφ
Σερίφ
Σχετικά με…
%s \n\nΣύνθεση
%s\n\nΠνευματικά δικαιώματα \u00A9 2017 Bill Farmer\n\nΕλληνική
μετάφραση από nikoss \n\nΆδεια GNU GPLv3
Πολύ μεγάλο %s
Επέλεξε ένα όνομα αρχείου
Φορτώνει…
Υπάρχουν μη αποθηκευμένες αλλαγές.\nΘα
ήθελες να αποθηκεύσεις τις αλλαγές σου;
Αυτό το αρχείο έχει αλλάξει.\nΘα ήθελες
να ξαναφορτωθεί;
Αυτό το αρχείο έχει αλλάξει.\nΘα
ήθελες να επανωγραφεί;
Εντάξει
Επαναφόρτωση
Επανωγραφή
Απόρριψη
Ακύρωση
================================================
FILE: src/main/res/values-es/strings.xml
================================================
Editor
Nuevo
Abrir archivo
Nombre
Camino
Crear
Editar
Ver
Abrir archivo…
Guardar
Almacenamiento
Abierto por última vez
Limpiar lista
Buscar…
Encuentra todos…
Guardar como…
Detectar
Vete a…
Imprimir…
Ver markdown…
Opciones
Mostrar archivo
Abrir último
Guardado automático
Salto de línea
Recomendaciones
Marcar la sintaxis
Tema
Claro
Oscuro
Sistema
Blanco
Negro
Retro
Tamaño del texto
Pequeño
Medio
Grande
Fuente
Monospace
Proporciónal
Palo seco
Serifa
Acerca de
%s \n\nBuild
%s\n\nCopyright \u00A9 2019 Bill Farmer\n\nTraducción al Español por
Miguel Caceres \n\nLicencia
GNU GPLv3
Demasiado grande %s
Ingresa el nombre del archivo
Cargando…
Cambios en el archivo no han sido guardados. Deseas gurdarlos?
El archivo ha sido modificado. Deseas actualizarlo?
El archivo ha sido modificado. Deseas sobreescribirlo?
OK
Actualizar
Sobreescribir
Descartar
Cancelar
================================================
FILE: src/main/res/values-eu/strings.xml
================================================
Editor
Berria
Fitxategia ireki
Izena
Bidea
Sortu
Editatu
Ikusi
Ireki fitxategia…
Gorde
Biltegiratzea
Ireki azkena
Garbitu zerrenda
Bilatu…
Aurkitu guztiak…
Gorde honela…
Atzeman
Joan hona…
Inprimatu…
Ikusi markdown…
Aukerak
Erakutsi fitxategia
Ireki azkena
Gorde automatikoki
Lerro-jausia
Gomendioak
Markatu sintaxia
Gaia
Argia
Iluna
Sistema
Zuria
Beltza
Retroa
Testuaren tamaina
Txikia
Tartekoa
Handia
Letra-tipoa
Monoespazioa
Proportzionala
Makila lehorra
Serifa
Honi buruz
%s \n\nBuild
%s\n\nCopyright \u00A9 2019 Bill Farmer\n\nEuskarara itzulia
Alexander Gabilondo \n\nLicentzia
GNU GPLv3
%s luzeegia
Sartu fitxategiaren izena
Kargatzen…
Fitxategiko aldaketak oraindik ez dira gorde. Gorde nahi dituzu?
Fitxategia aldatu da. Eguneratu nahi duzu?
Fitxategia aldatu da. Gainidatzi nahi duzu?
Ados
Eguneratu
Gainidatzi
Baztertu
Utzi
================================================
FILE: src/main/res/values-fa/strings.xml
================================================
ویرایشگر
جدید
باز کردن پرونده
نام
مسیر
ایجاد
ویرایش
نمایش
باز کردن پرونده…
ذخیره
انبار
اخیرا باز شده
پاک کردن فهرست
جستوجو
یافتن همه…
تشخیص
برو به…
ذخیره به عنوان…
چاپ…
نمایش مارکداون…
گزینه
نمایش پروندهها
باز کردن اخرین
ذخیره خودکار
پیچیدن کلمات
پیشنهادات
برجسته کردن سینتکس
شِما
روشن
تاریک
سیستم
سفید
سیاه
معکوس
اندازه قلم
کوچک
متوسط
بزرگ
سبک حروف
تک فاصله
متناسب
بدون سریف
سریف
درباره
%s \n\nساخته شده در %s\n\nحق چاپ \u00A9 ۲۰۱۷ Bill Farmer \n\nترجمه فرانسوی توسط Étienne Deparis , ترجمه آلمانی توسط Markus Weimar و Alexander Ritter , ترجمه ایتالیایی توسط Michael Moroni , ترجمه لهستانی توسط Evo , ترجمه یونانی توسط nikoss , ترجمه اسپانیایی توسط Miguel Caceres , ترجمه چینی ساده شده توسط Lu Chang (ludoux) , ترجمه هلندی توسط Heimen Stoffels (vistaus) , ترجمه روسی توسط Igor Nedoboy , ترجمه لتونیایی توسط Rihards Skuja , ترجمه ایسلندی توسط Sveinn í Felli , ترجمه پرتغالی توسط André Glória , ترجمه چینی (تایوان) توسط cges30901 , ترجمه ترکی توسط Yaşar Çiv , ترجمه پرتغالی برزیل توسط aevw , ترجمه کردی سورانی توسط Rawand Saeed , ترجمه فارسی توسط eshagh79 \n\nپروانه GNU GPLv3
خیلی بزرگ %s
یک نام برای پرونده انتخاب کنید
در حال بارگزاری…
شما تغییرات ذخیره نشدهای دارید. آیا می خواهید تغییرات خود را ذخیره کنید؟
این پرونده تغییر کرده است، آیا می خواهید آن را دوباره بارگزاری کنید؟
این پرونده تغییر کرده است، آیا می خواهید آن را بازنویسی کنید؟
خوب
بارگزاری مجدد
بازنویسی
دور انداختن
لغو
================================================
FILE: src/main/res/values-fr/strings.xml
================================================
Editor
Nouveau
Ouvrir un fichier
Nom
Chemin
Créer
Modifier
Voir
Ouvrir un fichier…
Enregistrer
Stockage
Fichiers récents
Effacer la liste
Chercher…
Chercher partout…
Enregistrer sous…
Détecter
Atteindre…
Imprimer…
Compiler markdown…
Options
Afficher les fichiers
Ouvrir en dernier
Enregistrement auto
Retour à la ligne auto
Suggestions
Surligner la syntaxe
Thème
Clair
Foncé
Système
Blanc
Noir
Rétro
Taille de la police
Petite
Moyenne
Grande
Type de police
Chasse fixe
Proportionnelle
Sans serif
Serif
À propos
%s \n\nCompilée le %s\n\nCopyright \u00A9 2017 Bill Farmer \n\nTraduction française par Étienne Deparis , German translation by Markus Weimar and Alexander Ritter , Italian translation by Michael Moroni , Polish translation by Evo , Greek translation by nikoss , Spanish translation by Miguel Caceres , Simplified Chinese translation by Lu Chang (ludoux) , Dutch translation by Heimen Stoffels (vistaus) , Russian translation by Igor Nedoboy , Latvian translation by Rihards Skuja , Icelandic translation by Sveinn í Felli , Portuguese translation by André Glória , Chinese (Taiwan) translation by cges30901 , Turkish translation by Yaşar Çiv , Brazilian Portuguese translation by aevw , Sorani Kurdish translation by Rawand Saeed , Persian (Farsi) translation by Eshagh \n\nLicence GNU GPLv3
Trop volumineux %s
Entrez le chemin complet d\'accès au fichier
Chargement…
Vous avez des changements non enregistrés. Souhaitez-vous les enregistrer ?
Ce fichier a changé, souhaitez-vous le recharger ?
Ce fichier a changé, souhaitez-vous l\'écraser ?
Ok
Recharger
Écraser
Abandonner
Annuler
================================================
FILE: src/main/res/values-hu/strings.xml
================================================
Szerkesztő
Új fájl
Fájl megnyitása
Név
Ösvény
Alkot
Szerkesztés
Megtekintés
Fájl megnyitása…
Mentés
Tároló
Legutóbbi megnyitása
Lista törlése
Keresés…
Összes keresése…
Mentés másként…
Felismerés
Odamegy…
Nyomtat…
Markdown megtekintése…
Beállítások
Fájlok megtekintése
Utolsó megnyitása
Automatikus mentés
Szövegtördelés
Javaslatok
Szintaxiskiemelés
Téma
Világos
Sötét
Rendszer
Fehér
Fekete
Retró
Szövegméret
Kicsi
Közepes
Nagy
Betűkészlet
Rögzített szélességű
Arányos
Groteszk
Betűtalp
Névjegy
%s \n\nÖsszeállítás: %s\n\nCopyright \u00A9 2017–2022 Bill Farmer \n\nMagyar fordítás: Úr Balázs \n\nLicenc: GNU GPLv3
Túl nagy: %s
Válasszon fájlnevet
Betöltés…
Mentetlen változtatásai vannak. Szeretné menteni a változtatásokat?
Ez a fájl megváltozott, szeretné újratölteni?
Ez a fájl megváltozott, szeretné felülírni?
Rendben
Újratöltés
Felülírás
Elvetés
Mégse
================================================
FILE: src/main/res/values-is/strings.xml
================================================
Ritill
Nýtt
Opna skrá
Nafn
Slóð
Skapa
Breyta
Skoða
Opna skrá…
Vista
Geymslurými
Opna nýlegt
Hreinsa lista
Leita…
Finndu alla…
Vista sem…
Greina
Farðu til…
Prenta…
Skoða markdown-kóða…
Valkostir
Skoða skrár
Opið síðast
Sjálfvirk vistun
Línuskrið orða
Tillögur
Hápunktur setningafræði
Þema
Ljóst
Dökkt
Kerfi
Hvítur
Svartur
Gamaldags
Stærð texta
Lítill
Miðlungs
Stór
Leturgerð
Jafnbreitt
Hlutfallslegt
Sans serif
Kólumbía
Um hugbúnaðinn
%s \n\nByggt
%s\n\nHöfundarréttur \u00A9 2017 Bill Farmer\n\nÍslensk þýðing: Sveinn í
Felli \n\nNotkunarleyfi GNU GPLv3
Of stór %s
Veldu skráarheiti
Hleð inn…
Þú ert með óvistaðar breytingar. Viltu vista þær?
Þessi skrá hefur breyst, viltu hlaða henni aftur inn?
Þessi skrá hefur breyst, viltu skrifa yfir hana?
Í lagi
Endurhlaða
Skrifa yfir
Henda
Hætta við
================================================
FILE: src/main/res/values-it/strings.xml
================================================
Editor
Nuovo
Apri file
Nome
Sentiero
Creare
Modificare
Vedere
Apri file…
Salva
Archiviazione
Apri recente
Elenco chiaro
Cerca…
Cerca ovunque…
Salva come…
Rileva
Vai a…
Stampa…
Visualizza markdown…
Opzioni
Vedi files
Aperto per ultimo
Salva automatico
A capo automatico
Suggerimenti
Evidenzia sintassi
Tema
Chiaro
Bianco
Scuro
Nero
Sistema
Retro
Dimensione testo
Piccola
Medio
Largo
Carattere
Monospazio
Proporzionale
Sans serif
Serif
Informazioni
%s \n\nData
di compilazione %s\n\nCopyright \u00A9 2017 Bill
Farmer\n\nTraduzione in italiano di Michael Moroni \n\nLicenza GNU GPLv3
Troppo grande %s
Scegli il nome del file
Caricamento…
Hai delle modifiche non salvate. Vuoi salva le tue modifiche?
Il file è cambiato. Vuoi ricaricarlo?
Il file è cambiato. Vuoi sovrascriverlo?
Ok
Ricarica
Sovrascrivi
Non salvare
Annulla
================================================
FILE: src/main/res/values-ja/strings.xml
================================================
エディター
新規ファイル
ファイルを開く
名前
パス
創造する
編集
閲覧
ファイルを開く…
保存
ストレージ
最近のファイル
リストをクリア
検索…
すべて検索
名前をつけて保存…
検出
行きます…
印刷…
マークダウンを閲覧…
オプション
ファイル閲覧
最後に開く
自動保存
画面端で折り返す
提案
シンタックスハイライト
テーマ
ライト
ダーク
制
白い
ブラック
レトロ
文字サイズ
小
中
大
書体
ミニバン
比例
サンセリフ体
セリフ
このアプリについて
%s \n\nBuilt %s\n\nCopyright \u00A9 2017 Bill Farmer \n\ndokutoku による日本語訳\n\nLicence GNU GPLv3
%sのサイズが大きすぎます
ファイル名を選択してください
読み込み中…
未保存の変更があります。変更を保存しますか?
このファイルは変更されましたが、再読み込みしますか?
このファイルは変更されましたが、上書きしますか?
はい
再読み込み
上書き
破棄
キャンセル
================================================
FILE: src/main/res/values-ku-rIQ/strings.xml
================================================
دەستکاریکەر
نوێ
فایل بکەرەوە
ناو
ڕێڕەو
پێکبهێنە
دەستکاریکردن
پیشاندان
فایل بکەرەوە
پاشەکەوتکردن
کۆگا
کردنەوەی نوێترین
لیستەکە پاکەرەوە
گەڕان..،
هەموو بدۆزەرەوە
پاشەکەوتکردن وەک
دۆزینەوە
بڕۆ بۆ…
چاپکردن…
مارکداون پیشانبدە
هەڵبژاردنەکان
فایلەکان پیشانبدە
دوایین کردنەوە
پاشەکەتکردنی ئۆتۆماتیک
پێچانەوەی ووشە
پێشنیازەکان
بەرچاوکردنی ڕستەنووسی
ڕووکار
کاڵ
تۆخ
سیستەم
سپی
ڕهش
ریترۆ
قەبارەی دەق
بچووک
مام ناوەند
گەورە
تایپفەیس
مۆنۆسپایس
هاوڕێژە
سانس سێریف
سێریف
سەبارەت
%s \n\nBuilt %s\n\nCopyright \u00A9 2017 Bill Farmer\n\nSorani Kurdish translation by Rawand Saeed \n\nLicence GNU GPLv3
خڕ %s
ناوی فایل هەڵبژێرە
لۆدین…
گۆڕانی پاشەکەتکردنت هەیە. دەتەوێت گۆڕانەکانت پاشەکەت بکەیت؟
فایلەکە گۆڕاوە، دەتەوێت باریبکەیتەوە؟
فایلەکە گۆڕاوە، دەتەوێث لەسەری بنووسیتەوە؟
OK
بارکردنەوە
نووسینەوەلەسەری
لابردن
هەڵوەشاندنەوە
================================================
FILE: src/main/res/values-lv/strings.xml
================================================
Redaktors
Jauns
Atvērt failu
Vārds
Ceļu
Radīt
Rediģēt
Skatīt
Atvērt failu…
Saglabāt
Krātuve
Atvērt neseno
Notīrīt sarakstu
Meklēt…
Atrodi visu…
Saglabāt kā…
Atklāt
Doties uz…
Drukāt…
Skatīt markdown…
Opcijas
Skatīt failus
Atvērt pēdējo
Automātiska saglabāšana
Vārdu aplaušana
Ieteikumi
Izcelt sintaksi
Motīvs
Gaišs
Tumšs
Sistēma
Balts
Melns
Retro
Teksta izmērs
Mazs
Vidējs
Liels
Burtveidols
Vienplatuma
Proporcionāls
Sans serifs
Serif
Par
%s \n\nBūvēts
%s\n\nAutortiesības \u00A9 2017 Bill Farmer\n\nNo latviešu valodas
tulkojis Rihards Skuja
\n\nLicence GNU
GPLv3
Pārāk liels %s
Izvēlies faila nosaukumu
Atver…
Tev ir nesaglabātas izmaiņas. Vai vēlies tās saglabāt?
Šis fails ir izmainījies. Vai vēlies to pārlādēt?
Šis fails ir izmainījies. Vai vēlies to pārrakstīt?
Labi
Pārlādēt
Pārrakstīt
Atmest izmaiņas
Atcelt
================================================
FILE: src/main/res/values-nl/strings.xml
================================================
Tekstbewerker
Nieuw
Bestand openen
Naam
Pad
Scheppen
Bewerken
Tonen
Bestand openen…
Opslaan
Opslagruimte
Recent bestand openen
Lijst wissen
Zoeken…
Alles zoeken…
Detecteren
Ga naar…
Opslaan als…
Afdrukken…
Markdown tonen…
Opties
Bestanden tonen
Laatste openen
Automatisch opslaan
Tekstomloop
Suggesties
Syntaxis markeren
Thema
Licht
Donker
Systeem
Wit
Zwart
Retro
Lettergrootte
Klein
Normaal
Groot
Lettertype
Vaste breedte
Proportioneel
Schreefloos
Schreef
Over
%s \n\nGebouwd
op %s\n\nCopyright \u00A9 2017 Bill Farmer\n\nNederlandse vertaling
door Heimen Stoffels
(vistaus) \n\nLicentie GNU GPLv3
Te groot %s
Kies een bestandsnaam
Bezig met laden…
Je hebt niet-opgeslagen wijzigingen. Wil je deze opslaan?
Het bestand is gewijzigd; wil je het herladen?
Het bestand is gewijzigd; wil je het overschrijven?
Oké
Herladen
Overschrijven
Verwerpen
Annuleren
================================================
FILE: src/main/res/values-pl/strings.xml
================================================
Editor
Nowy
Otwórz plik
Nazwa
Ścieżka
Tworzyć
Edytuj
Podgląd
Otwórz plik…
Zapisz
Pamięć wewnętrzna
Otwórz poprzedni
Czysta lista
Szukaj…
Znajdź wszystkie…
Zapisz jako…
Wykryć
Przejdź do…
Wydrukować…
Wyświetl markdown…
Opcje
Pokaż pliki
Otwarte jako ostatnie
Autozapis
Zawijanie słów
Sugestie
Podświetl składnię
Motyw
Jasny
Ciemny
System
Biały
Czarny
Retro
Rozmiar czcionki
Mały
Średni
Duży
Czcionka
Monospace
Proporcjonalny
Bezszeryfowy
Szeryf
O programie
%s \n\nData
kompilacji %s\n\nCopyright \u00A9 2017 Bill Farmer\n\nPolskie
tłumaczenie Evo \n\nLicencja
GNU GPLv3
Za duży %s
Wybierz nazwę pliku
Ładowanie…
Masz niezapisane zmiany. Czy chcesz je zapisać?
Plik uległ zmianie, czy chcesz go wczytać ponownie?
Plik uległ zmianie, czy chcesz go nadpisać?
OK
Wczytaj ponownie
Nadpisz
Porzuć
Anuluj
================================================
FILE: src/main/res/values-pt/strings.xml
================================================
Editor
Novo
Abrir ficheiro
Designação
Caminho
Criar
Editar
Ver
Abrir ficheiro…
Guardar
Armazenamento
Abrir recente
Limpar lista
Pesquisar…
Pesquisar tudo…
Guardar como…
Detetar
Ir para…
Impressão…
Ver markdown…
Opções
Ver ficheiros
Abrir por último
Guardar automático
Quebras de linha
Mostrar sugestões
Destacar sintaxe
Tema
Claro
Escuro
Branco
Preto
Sistema
Retro
Tamanho das letras
Pequeno
Médio
Grande
Espaçamento
Monoespaçado
Proporcional
Sem serifa
Serif
Sobre
%s \n\nCompilado
a %s\n\nDireitos de autor \u00A9 2017 Bill Farmer \n\nTradução Portuguesa por
André
Glória e laralem \n\nLicença GNU GPLv3
Demasiado grande %s
Escolha um nome de ficheiro
A carregar…
Tem alterações por guardar. Quer guardá-las?
Este ficheiro foi alterado, quer tornar a abri-lo?
Este ficheiro foi alterado, quer substituí-lo (perderá as alterações)?
OK
Reabrir
Substituir
Descartar
Cancelar
================================================
FILE: src/main/res/values-pt-rBR/strings.xml
================================================
Editor
Novo
Abrir arquivo
Nome
Caminho
Criar
Editar
Ver
Abrir arquivo…
Salvar
Armazenamento
Abrir recente
Limpar lista
Procurar…
Encontrar todos…
Salvar como…
Detectar
Ir para…
Impressão…
Ver markdown…
Opções
Ver arquivos
Abrir por último
Auto salvar
Quebra as linhas
Sugestões
Destacar a sintaxe
Tema
Claro
Branco
Escuro
Sistema
Retrô
Tamanho do texto
Pequeno
Médio
Grande
Tipo da fonte
Monospace
Proporcional
Sem serifa
Serif
Sobre
%s \n\nCompilado %s\n\nCopyright \u00A9 2017 Bill Farmer \n\nTradução para português brasileiro por aevw \n\nLicença GNU GPLv3
Muito grande %s
Digite um nome de arquivo
Abrindo…
Tem alterações não salvas. Você quer salvar?
Este arquivo foi modificado, quer atualizar?
Este arquivo foi modificado, você quer substituí-lo (perderá alterações)?
OK
Recarregar
Substituir
Descartar
Cancelar
================================================
FILE: src/main/res/values-ru/strings.xml
================================================
Редактор
Поиск…
Открыть файл
Имя
Путь
Создавать
Найти все…
Новый
Редактор
Просмотр
Открыть файл…
Сохранить
Хранилище
Недавние
Очистить список
Сохранить как…
Обнаруживать
Перейти к…
Печатать…
Просмотр Markdown…
Параметры
Просмотр файлов
Открыт последним
Автосохранение
Перенос строк
Варианты слов
Выделить синтаксис
Тема
Светлая
Тёмная
Белая
Чёрная
Системная
Ретро
Размер шрифта
Малый
Средний
Крупный
Шрифт
Моноширинный
Пропорциональный
Без засечек
С засечками
О приложении
%s \n\nСборка
%s\nРазработка \u00A9 2017 Bill Farmer\n\nРусский перевод \u00A9 Igor Nedoboy \n\nЛицензия GNU GPLv3
Слишком большой %s
Выберите имя файла
Чтение…
Сохранить изменения?
Файл изменился, перечитать?
Файл изменился, перезаписать?
ОК
Перечитать
Перезаписать
Не сохранять
Отмена
================================================
FILE: src/main/res/values-tr/strings.xml
================================================
Düzenleyici
Yeni
Dosya aç
Ad
Yol
Yaratmak
Düzenle
Görünüm
Dosya aç…
Kaydet
Depolama
Son kullanılanı aç
Listeyi temizle
Ara…
Hepsini bul…
Farklı kaydet…
Algılamak
Git…
Yazdır…
Markdown görünümü…
Seçenekler
Dosyaları görüntüle
En son aç
Otomatik kaydet
Sözcük kaydırma
Öneriler
Sözdizimini vurgula
Tema
Açık
Koyu
Beyaz
Siyah
Sistem
Nostaljik
Metin boyutu
Küçük
Orta
Büyük
Harf karakteri
Eş aralıklı
Orantılı
Sans serif
Serif
Hakkında
%s \n\n%s
tarihinde oluşturuldu\n\nTelif Hakkı \u00A9 2017 Bill
Farmer\n\nTürkçe çeviri Yaşar Çiv tarafından
yapıldı\n\nLisans GNU
GPLv3
Çok büyük %s
Bir dosya adı seçin
Yükleniyor…
Kaydedilmemiş değişiklikleriniz mevcut. Değişikliklerinizi kaydetmek ister misiniz?
Bu dosya değişti, yeniden yüklemek ister misiniz?
Bu dosya değişti, üzerine yazmak istiyor musunuz?
Tamam
Yeniden yükle
Üzerine yaz
Gözardı et
İptal et
================================================
FILE: src/main/res/values-uk/strings.xml
================================================
Редактор
Новий файл
Відкрити файл
Назва
Шлях
Творити
Змінити
Переглянути
Відкрити файл…
Зберегти
Сховище
Відкрити останні
Очистити список
Пошук…
Шукати все…
Зберегти як…
Виявити
Перейти до…
Друк…
Переглянути markdown…
Параметри
Переглянути файли
Відкрити останню
Автозбереження
Перенесення слів
Пропозиції
Підсвітка синтаксису
Тема
Світла
Темна
Система
Біла
Чорна
Ретро
Розмір шрифту
Малий
Середній
Великий
Шрифт
Моноширний
Пропорційний
Без засічок
Із засічками
Про застосунок
%s \n\nЗбірка %s\n\nАвторські права \u00A9 2017 Bill Farmer \n\nFrench translation by Étienne Deparis , German translation by Markus Weimar and Alexander Ritter , Italian translation by Michael Moroni , Polish translation by Evo , Greek translation by nikoss , Spanish translation by Miguel Caceres , Simplified Chinese translation by Lu Chang (ludoux) , Dutch translation by Heimen Stoffels (vistaus) , Russian translation by Igor Nedoboy , Latvian translation by Rihards Skuja , Icelandic translation by Sveinn í Felli , Portuguese translation by André Glória , Chinese (Taiwan) translation by cges30901 , Turkish translation by Yaşar Çiv , Brazilian Portuguese translation by aevw , Sorani Kurdish translation by Rawand Saeed , Persian (Farsi) translation by Eshagh , Hungarian translation by Balázs Úr , Japanese translation by dokutoku , Українською переклав Andrij Mizyk \n\nЛіцензія GNU GPLv3
Задовга %s
Оберіть назву файлу
Завантаження…
У Вас не збережені зміни. Бажаєте зберегти їх?
Цей файл змінено, бажаєте перезавантажити його?
Цей файл змінено, бажаєте перезаписати його?
Гаразд
Перезавантажити
Перезаписати
Відхилити
Скасувати
================================================
FILE: src/main/res/values-zh-rCN/strings.xml
================================================
编辑器
新
打开文件
名字
路径
创造
编辑
查看
打开文件…
保存
存储空间
打开最近文本
清除列表
查找…
全局搜索…
保存为…
检测
转到…
打印…
查看 Markdown…
选项
查看文件
最后打开
自动保存
自动换行
建议
突出显示语法
主题
亮
暗
系统
白
黑色的
复古
文本大小
小
中
大
字体
等宽
变宽
无衬线
衬线
关于
%s \n\n
编译时间 %s\n\n版权 \u00A9 2017 Bill Farmer\n\n中文(简体)译者Lu Chang (ludoux) \n\n开源协议GNU GPLv3
太大了 %s
选择一个文件
加载中…
你有未保存的更改。你想保存这些更改吗?
文件已被更改,你想重载它吗?
文件已被更改,你想覆写它吗?
OK
重载
覆写
放弃更改
取消
================================================
FILE: src/main/res/values-zh-rTW/strings.xml
================================================
編輯器
新增
開啟檔案
名字
路径
创造
編輯
檢視
開啟檔案…
儲存
儲存空間
最近開啟
清除清單
搜尋…
尋找全部…
儲存為…
检测
转到…
打印…
檢視 markdown…
选项
檢視檔案
最后打开
自動儲存
文字換列
建議
突顯語法
主題
亮色
暗色
系统
白
黑色
復古
文字大小
小
中
大
字體
等寬字體
比例字體
无衬线
衬线
關於
%s \n\n
組建時間 %s\n\n版權 \u00A9 2017 Bill Farmer\n\n中文(繁體)譯者:張修銘 \n\n授權 GNU GPLv3
太大了 %s
選擇檔案名稱
載入中…
有未儲存的變更。您要儲存變更嗎?
檔案已變更。您要重新載入嗎?
檔案已變更。您要覆寫它嗎?
確定
重新載入
覆寫
捨棄
取消
================================================
FILE: src/main/res/xml/filepaths.xml
================================================
================================================
FILE: src/main/res/xml/shortcuts.xml
================================================