diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:53 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:53 -0800 |
commit | 1edb39f280d23b3a87db45b63c2f26850d68eafe (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /src/com/android/providers/downloads/Helpers.java | |
parent | 96d15ef26e4ac2d15bb11c327a8a04027032bab7 (diff) | |
download | android_packages_providers_DownloadProvider-1edb39f280d23b3a87db45b63c2f26850d68eafe.tar.gz android_packages_providers_DownloadProvider-1edb39f280d23b3a87db45b63c2f26850d68eafe.tar.bz2 android_packages_providers_DownloadProvider-1edb39f280d23b3a87db45b63c2f26850d68eafe.zip |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'src/com/android/providers/downloads/Helpers.java')
-rw-r--r-- | src/com/android/providers/downloads/Helpers.java | 793 |
1 files changed, 0 insertions, 793 deletions
diff --git a/src/com/android/providers/downloads/Helpers.java b/src/com/android/providers/downloads/Helpers.java deleted file mode 100644 index 7c6070f3..00000000 --- a/src/com/android/providers/downloads/Helpers.java +++ /dev/null @@ -1,793 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * 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 com.android.providers.downloads; - -import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.database.Cursor; -import android.drm.mobile1.DrmRawContent; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.Uri; -import android.os.Environment; -import android.os.StatFs; -import android.os.SystemClock; -import android.provider.Downloads; -import android.telephony.TelephonyManager; -import android.util.Config; -import android.util.Log; -import android.webkit.MimeTypeMap; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.util.List; -import java.util.Random; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.Set; - -/** - * Some helper functions for the download manager - */ -public class Helpers { - - public static Random rnd = new Random(SystemClock.uptimeMillis()); - - /** Regex used to parse content-disposition headers */ - private static final Pattern CONTENT_DISPOSITION_PATTERN = - Pattern.compile("attachment;\\s*filename\\s*=\\s*\"([^\"]*)\""); - - private Helpers() { - } - - /* - * Parse the Content-Disposition HTTP Header. The format of the header - * is defined here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html - * This header provides a filename for content that is going to be - * downloaded to the file system. We only support the attachment type. - */ - private static String parseContentDisposition(String contentDisposition) { - try { - Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition); - if (m.find()) { - return m.group(1); - } - } catch (IllegalStateException ex) { - // This function is defined as returning null when it can't parse the header - } - return null; - } - - /** - * Creates a filename (where the file should be saved) from a uri. - */ - public static DownloadFileInfo generateSaveFile( - Context context, - String url, - String hint, - String contentDisposition, - String contentLocation, - String mimeType, - int destination, - int contentLength) throws FileNotFoundException { - - /* - * Don't download files that we won't be able to handle - */ - if (destination == Downloads.DESTINATION_EXTERNAL - || destination == Downloads.DESTINATION_CACHE_PARTITION_PURGEABLE) { - if (mimeType == null) { - if (Config.LOGD) { - Log.d(Constants.TAG, "external download with no mime type not allowed"); - } - return new DownloadFileInfo(null, null, Downloads.STATUS_NOT_ACCEPTABLE); - } - if (!DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(mimeType)) { - // Check to see if we are allowed to download this file. Only files - // that can be handled by the platform can be downloaded. - // special case DRM files, which we should always allow downloading. - Intent intent = new Intent(Intent.ACTION_VIEW); - - // We can provide data as either content: or file: URIs, - // so allow both. (I think it would be nice if we just did - // everything as content: URIs) - // Actually, right now the download manager's UId restrictions - // prevent use from using content: so it's got to be file: or - // nothing - - PackageManager pm = context.getPackageManager(); - intent.setDataAndType(Uri.fromParts("file", "", null), mimeType); - ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); - //Log.i(Constants.TAG, "*** FILENAME QUERY " + intent + ": " + list); - - if (ri == null) { - if (Config.LOGD) { - Log.d(Constants.TAG, "no handler found for type " + mimeType); - } - return new DownloadFileInfo(null, null, Downloads.STATUS_NOT_ACCEPTABLE); - } - } - } - String filename = chooseFilename( - url, hint, contentDisposition, contentLocation, destination); - - // Split filename between base and extension - // Add an extension if filename does not have one - String extension = null; - int dotIndex = filename.indexOf('.'); - if (dotIndex < 0) { - extension = chooseExtensionFromMimeType(mimeType, true); - } else { - extension = chooseExtensionFromFilename( - mimeType, destination, filename, dotIndex); - filename = filename.substring(0, dotIndex); - } - - /* - * Locate the directory where the file will be saved - */ - - File base = null; - StatFs stat = null; - // DRM messages should be temporarily stored internally and then passed to - // the DRM content provider - if (destination == Downloads.DESTINATION_CACHE_PARTITION - || destination == Downloads.DESTINATION_CACHE_PARTITION_PURGEABLE - || destination == Downloads.DESTINATION_CACHE_PARTITION_NOROAMING - || DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(mimeType)) { - base = Environment.getDownloadCacheDirectory(); - stat = new StatFs(base.getPath()); - - /* - * Check whether there's enough space on the target filesystem to save the file. - * Put a bit of margin (in case creating the file grows the system by a few blocks). - */ - int blockSize = stat.getBlockSize(); - for (;;) { - int availableBlocks = stat.getAvailableBlocks(); - if (blockSize * ((long) availableBlocks - 4) >= contentLength) { - break; - } - if (!discardPurgeableFiles(context, - contentLength - blockSize * ((long) availableBlocks - 4))) { - if (Config.LOGD) { - Log.d(Constants.TAG, - "download aborted - not enough free space in internal storage"); - } - return new DownloadFileInfo(null, null, Downloads.STATUS_FILE_ERROR); - } - stat.restat(base.getPath()); - } - - } else { - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - String root = Environment.getExternalStorageDirectory().getPath(); - base = new File(root + Constants.DEFAULT_DL_SUBDIR); - if (!base.isDirectory() && !base.mkdir()) { - if (Config.LOGD) { - Log.d(Constants.TAG, "download aborted - can't create base directory " - + base.getPath()); - } - return new DownloadFileInfo(null, null, Downloads.STATUS_FILE_ERROR); - } - stat = new StatFs(base.getPath()); - } else { - if (Config.LOGD) { - Log.d(Constants.TAG, "download aborted - no external storage"); - } - return new DownloadFileInfo(null, null, Downloads.STATUS_FILE_ERROR); - } - - /* - * Check whether there's enough space on the target filesystem to save the file. - * Put a bit of margin (in case creating the file grows the system by a few blocks). - */ - if (stat.getBlockSize() * ((long) stat.getAvailableBlocks() - 4) < contentLength) { - if (Config.LOGD) { - Log.d(Constants.TAG, "download aborted - not enough free space"); - } - return new DownloadFileInfo(null, null, Downloads.STATUS_FILE_ERROR); - } - - } - - boolean recoveryDir = Constants.RECOVERY_DIRECTORY.equalsIgnoreCase(filename + extension); - - filename = base.getPath() + File.separator + filename; - - /* - * Generate a unique filename, create the file, return it. - */ - if (Constants.LOGVV) { - Log.v(Constants.TAG, "target file: " + filename + extension); - } - - String fullFilename = chooseUniqueFilename( - destination, filename, extension, recoveryDir); - if (fullFilename != null) { - return new DownloadFileInfo(fullFilename, new FileOutputStream(fullFilename), 0); - } else { - return new DownloadFileInfo(null, null, Downloads.STATUS_FILE_ERROR); - } - } - - private static String chooseFilename(String url, String hint, String contentDisposition, - String contentLocation, int destination) { - String filename = null; - - // First, try to use the hint from the application, if there's one - if (filename == null && hint != null && !hint.endsWith("/")) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "getting filename from hint"); - } - int index = hint.lastIndexOf('/') + 1; - if (index > 0) { - filename = hint.substring(index); - } else { - filename = hint; - } - } - - // If we couldn't do anything with the hint, move toward the content disposition - if (filename == null && contentDisposition != null) { - filename = parseContentDisposition(contentDisposition); - if (filename != null) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "getting filename from content-disposition"); - } - int index = filename.lastIndexOf('/') + 1; - if (index > 0) { - filename = filename.substring(index); - } - } - } - - // If we still have nothing at this point, try the content location - if (filename == null && contentLocation != null) { - String decodedContentLocation = Uri.decode(contentLocation); - if (decodedContentLocation != null - && !decodedContentLocation.endsWith("/") - && decodedContentLocation.indexOf('?') < 0) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "getting filename from content-location"); - } - int index = decodedContentLocation.lastIndexOf('/') + 1; - if (index > 0) { - filename = decodedContentLocation.substring(index); - } else { - filename = decodedContentLocation; - } - } - } - - // If all the other http-related approaches failed, use the plain uri - if (filename == null) { - String decodedUrl = Uri.decode(url); - if (decodedUrl != null - && !decodedUrl.endsWith("/") && decodedUrl.indexOf('?') < 0) { - int index = decodedUrl.lastIndexOf('/') + 1; - if (index > 0) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "getting filename from uri"); - } - filename = decodedUrl.substring(index); - } - } - } - - // Finally, if couldn't get filename from URI, get a generic filename - if (filename == null) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "using default filename"); - } - filename = Constants.DEFAULT_DL_FILENAME; - } - - filename = filename.replaceAll("[^a-zA-Z0-9\\.\\-_]+", "_"); - - - return filename; - } - - private static String chooseExtensionFromMimeType(String mimeType, boolean useDefaults) { - String extension = null; - if (mimeType != null) { - extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType); - if (extension != null) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "adding extension from type"); - } - extension = "." + extension; - } else { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "couldn't find extension for " + mimeType); - } - } - } - if (extension == null) { - if (mimeType != null && mimeType.toLowerCase().startsWith("text/")) { - if (mimeType.equalsIgnoreCase("text/html")) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "adding default html extension"); - } - extension = Constants.DEFAULT_DL_HTML_EXTENSION; - } else if (useDefaults) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "adding default text extension"); - } - extension = Constants.DEFAULT_DL_TEXT_EXTENSION; - } - } else if (useDefaults) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "adding default binary extension"); - } - extension = Constants.DEFAULT_DL_BINARY_EXTENSION; - } - } - return extension; - } - - private static String chooseExtensionFromFilename(String mimeType, int destination, - String filename, int dotIndex) { - String extension = null; - if (mimeType != null) { - // Compare the last segment of the extension against the mime type. - // If there's a mismatch, discard the entire extension. - int lastDotIndex = filename.lastIndexOf('.'); - String typeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension( - filename.substring(lastDotIndex + 1)); - if (typeFromExt == null || !typeFromExt.equalsIgnoreCase(mimeType)) { - extension = chooseExtensionFromMimeType(mimeType, false); - if (extension != null) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "substituting extension from type"); - } - } else { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "couldn't find extension for " + mimeType); - } - } - } - } - if (extension == null) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "keeping extension"); - } - extension = filename.substring(dotIndex); - } - return extension; - } - - private static String chooseUniqueFilename(int destination, String filename, - String extension, boolean recoveryDir) { - String fullFilename = filename + extension; - if (!new File(fullFilename).exists() - && (!recoveryDir || - (destination != Downloads.DESTINATION_CACHE_PARTITION && - destination != Downloads.DESTINATION_CACHE_PARTITION_PURGEABLE && - destination != Downloads.DESTINATION_CACHE_PARTITION_NOROAMING))) { - return fullFilename; - } - filename = filename + Constants.FILENAME_SEQUENCE_SEPARATOR; - /* - * This number is used to generate partially randomized filenames to avoid - * collisions. - * It starts at 1. - * The next 9 iterations increment it by 1 at a time (up to 10). - * The next 9 iterations increment it by 1 to 10 (random) at a time. - * The next 9 iterations increment it by 1 to 100 (random) at a time. - * ... Up to the point where it increases by 100000000 at a time. - * (the maximum value that can be reached is 1000000000) - * As soon as a number is reached that generates a filename that doesn't exist, - * that filename is used. - * If the filename coming in is [base].[ext], the generated filenames are - * [base]-[sequence].[ext]. - */ - int sequence = 1; - for (int magnitude = 1; magnitude < 1000000000; magnitude *= 10) { - for (int iteration = 0; iteration < 9; ++iteration) { - fullFilename = filename + sequence + extension; - if (!new File(fullFilename).exists()) { - return fullFilename; - } - if (Constants.LOGVV) { - Log.v(Constants.TAG, "file with sequence number " + sequence + " exists"); - } - sequence += rnd.nextInt(magnitude) + 1; - } - } - return null; - } - - /** - * Deletes purgeable files from the cache partition. This also deletes - * the matching database entries. Files are deleted in LRU order until - * the total byte size is greater than targetBytes. - */ - public static final boolean discardPurgeableFiles(Context context, long targetBytes) { - Cursor cursor = context.getContentResolver().query( - Downloads.CONTENT_URI, - null, - "( " + - Downloads.STATUS + " = " + Downloads.STATUS_SUCCESS + " AND " + - Downloads.DESTINATION + " = " + Downloads.DESTINATION_CACHE_PARTITION_PURGEABLE - + " )", - null, - Downloads.LAST_MODIFICATION); - if (cursor == null) { - return false; - } - long totalFreed = 0; - try { - cursor.moveToFirst(); - while (!cursor.isAfterLast() && totalFreed < targetBytes) { - File file = new File(cursor.getString(cursor.getColumnIndex(Downloads._DATA))); - if (Constants.LOGVV) { - Log.v(Constants.TAG, "purging " + file.getAbsolutePath() + " for " + - file.length() + " bytes"); - } - totalFreed += file.length(); - file.delete(); - long id = cursor.getLong(cursor.getColumnIndex(Downloads._ID)); - context.getContentResolver().delete( - ContentUris.withAppendedId(Downloads.CONTENT_URI, id), null, null); - cursor.moveToNext(); - } - } finally { - cursor.close(); - } - if (Constants.LOGV) { - if (totalFreed > 0) { - Log.v(Constants.TAG, "Purged files, freed " + totalFreed + " for " + - targetBytes + " requested"); - } - } - return totalFreed > 0; - } - - /** - * Returns whether the network is available - */ - public static boolean isNetworkAvailable(Context context) { - ConnectivityManager connectivity = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (connectivity == null) { - Log.w(Constants.TAG, "couldn't get connectivity manager"); - } else { - NetworkInfo[] info = connectivity.getAllNetworkInfo(); - if (info != null) { - for (int i = 0; i < info.length; i++) { - if (info[i].getState() == NetworkInfo.State.CONNECTED) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "network is available"); - } - return true; - } - } - } - } - if (Constants.LOGVV) { - Log.v(Constants.TAG, "network is not available"); - } - return false; - } - - /** - * Returns whether the network is roaming - */ - public static boolean isNetworkRoaming(Context context) { - ConnectivityManager connectivity = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (connectivity == null) { - Log.w(Constants.TAG, "couldn't get connectivity manager"); - } else { - NetworkInfo info = connectivity.getActiveNetworkInfo(); - if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE) { - if (TelephonyManager.getDefault().isNetworkRoaming()) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "network is roaming"); - } - return true; - } else { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "network is not roaming"); - } - } - } else { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "not using mobile network"); - } - } - } - return false; - } - - /** - * Checks whether the filename looks legitimate - */ - public static boolean isFilenameValid(String filename) { - File dir = new File(filename).getParentFile(); - return dir.equals(Environment.getDownloadCacheDirectory()) - || dir.equals(new File(Environment.getExternalStorageDirectory() - + Constants.DEFAULT_DL_SUBDIR)); - } - - /** - * Checks whether this looks like a legitimate selection parameter - */ - public static void validateSelection(String selection, Set<String> allowedColumns) { - try { - if (selection == null) { - return; - } - Lexer lexer = new Lexer(selection, allowedColumns); - parseExpression(lexer); - if (lexer.currentToken() != Lexer.TOKEN_END) { - throw new IllegalArgumentException("syntax error"); - } - } catch (RuntimeException ex) { - if (Constants.LOGV) { - Log.d(Constants.TAG, "invalid selection [" + selection + "] triggered " + ex); - } else if (Config.LOGD) { - Log.d(Constants.TAG, "invalid selection triggered " + ex); - } - throw ex; - } - - } - - // expression <- ( expression ) | statement [AND_OR ( expression ) | statement] * - // | statement [AND_OR expression]* - private static void parseExpression(Lexer lexer) { - for (;;) { - // ( expression ) - if (lexer.currentToken() == Lexer.TOKEN_OPEN_PAREN) { - lexer.advance(); - parseExpression(lexer); - if (lexer.currentToken() != Lexer.TOKEN_CLOSE_PAREN) { - throw new IllegalArgumentException("syntax error, unmatched parenthese"); - } - lexer.advance(); - } else { - // statement - parseStatement(lexer); - } - if (lexer.currentToken() != Lexer.TOKEN_AND_OR) { - break; - } - lexer.advance(); - } - } - - // statement <- COLUMN COMPARE VALUE - // | COLUMN IS NULL - private static void parseStatement(Lexer lexer) { - // both possibilities start with COLUMN - if (lexer.currentToken() != Lexer.TOKEN_COLUMN) { - throw new IllegalArgumentException("syntax error, expected column name"); - } - lexer.advance(); - - // statement <- COLUMN COMPARE VALUE - if (lexer.currentToken() == Lexer.TOKEN_COMPARE) { - lexer.advance(); - if (lexer.currentToken() != Lexer.TOKEN_VALUE) { - throw new IllegalArgumentException("syntax error, expected quoted string"); - } - lexer.advance(); - return; - } - - // statement <- COLUMN IS NULL - if (lexer.currentToken() == Lexer.TOKEN_IS) { - lexer.advance(); - if (lexer.currentToken() != Lexer.TOKEN_NULL) { - throw new IllegalArgumentException("syntax error, expected NULL"); - } - lexer.advance(); - return; - } - - // didn't get anything good after COLUMN - throw new IllegalArgumentException("syntax error after column name"); - } - - /** - * A simple lexer that recognizes the words of our restricted subset of SQL where clauses - */ - private static class Lexer { - public static final int TOKEN_START = 0; - public static final int TOKEN_OPEN_PAREN = 1; - public static final int TOKEN_CLOSE_PAREN = 2; - public static final int TOKEN_AND_OR = 3; - public static final int TOKEN_COLUMN = 4; - public static final int TOKEN_COMPARE = 5; - public static final int TOKEN_VALUE = 6; - public static final int TOKEN_IS = 7; - public static final int TOKEN_NULL = 8; - public static final int TOKEN_END = 9; - - private final String mSelection; - private final Set<String> mAllowedColumns; - private int mOffset = 0; - private int mCurrentToken = TOKEN_START; - private final char[] mChars; - - public Lexer(String selection, Set<String> allowedColumns) { - mSelection = selection; - mAllowedColumns = allowedColumns; - mChars = new char[mSelection.length()]; - mSelection.getChars(0, mChars.length, mChars, 0); - advance(); - } - - public int currentToken() { - return mCurrentToken; - } - - public void advance() { - char[] chars = mChars; - - // consume whitespace - while (mOffset < chars.length && chars[mOffset] == ' ') { - ++mOffset; - } - - // end of input - if (mOffset == chars.length) { - mCurrentToken = TOKEN_END; - return; - } - - // "(" - if (chars[mOffset] == '(') { - ++mOffset; - mCurrentToken = TOKEN_OPEN_PAREN; - return; - } - - // ")" - if (chars[mOffset] == ')') { - ++mOffset; - mCurrentToken = TOKEN_CLOSE_PAREN; - return; - } - - // "?" - if (chars[mOffset] == '?') { - ++mOffset; - mCurrentToken = TOKEN_VALUE; - return; - } - - // "=" and "==" - if (chars[mOffset] == '=') { - ++mOffset; - mCurrentToken = TOKEN_COMPARE; - if (mOffset < chars.length && chars[mOffset] == '=') { - ++mOffset; - } - return; - } - - // ">" and ">=" - if (chars[mOffset] == '>') { - ++mOffset; - mCurrentToken = TOKEN_COMPARE; - if (mOffset < chars.length && chars[mOffset] == '=') { - ++mOffset; - } - return; - } - - // "<", "<=" and "<>" - if (chars[mOffset] == '<') { - ++mOffset; - mCurrentToken = TOKEN_COMPARE; - if (mOffset < chars.length && (chars[mOffset] == '=' || chars[mOffset] == '>')) { - ++mOffset; - } - return; - } - - // "!=" - if (chars[mOffset] == '!') { - ++mOffset; - mCurrentToken = TOKEN_COMPARE; - if (mOffset < chars.length && chars[mOffset] == '=') { - ++mOffset; - return; - } - throw new IllegalArgumentException("Unexpected character after !"); - } - - // columns and keywords - // first look for anything that looks like an identifier or a keyword - // and then recognize the individual words. - // no attempt is made at discarding sequences of underscores with no alphanumeric - // characters, even though it's not clear that they'd be legal column names. - if (isIdentifierStart(chars[mOffset])) { - int startOffset = mOffset; - ++mOffset; - while (mOffset < chars.length && isIdentifierChar(chars[mOffset])) { - ++mOffset; - } - String word = mSelection.substring(startOffset, mOffset); - if (mOffset - startOffset <= 4) { - if (word.equals("IS")) { - mCurrentToken = TOKEN_IS; - return; - } - if (word.equals("OR") || word.equals("AND")) { - mCurrentToken = TOKEN_AND_OR; - return; - } - if (word.equals("NULL")) { - mCurrentToken = TOKEN_NULL; - return; - } - } - if (mAllowedColumns.contains(word)) { - mCurrentToken = TOKEN_COLUMN; - return; - } - throw new IllegalArgumentException("unrecognized column or keyword"); - } - - // quoted strings - if (chars[mOffset] == '\'') { - ++mOffset; - while(mOffset < chars.length) { - if (chars[mOffset] == '\'') { - if (mOffset + 1 < chars.length && chars[mOffset + 1] == '\'') { - ++mOffset; - } else { - break; - } - } - ++mOffset; - } - if (mOffset == chars.length) { - throw new IllegalArgumentException("unterminated string"); - } - ++mOffset; - mCurrentToken = TOKEN_VALUE; - return; - } - - // anything we don't recognize - throw new IllegalArgumentException("illegal character"); - } - - private static final boolean isIdentifierStart(char c) { - return c == '_' || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z'); - } - - private static final boolean isIdentifierChar(char c) { - return c == '_' || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - (c >= '0' && c <= '9'); - } - } -} |