001/**
002 * Java Web Archive Toolkit - Software to read and validate ARC, WARC
003 * and GZip files. (http://jwat.org/)
004 * Copyright 2011-2012 Netarkivet.dk (http://netarkivet.dk/)
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.jwat.common;
019
020/**
021 * Simple class to validate a scheme in the beginning of a byte array.
022 *
023 * @author nicl
024 */
025public class Scheme {
026
027    /** Bit field used to identify valid first/follow characters. */
028    protected static int[] bf = new int[256];
029
030    /**
031     * Disable instantiation.
032     */
033    protected Scheme() {
034    }
035
036    /*
037     * Initialize first/follow bit field.
038     */
039    static {
040        String alphas = "abcdefghijklmnopqrstuvwxyz";
041        for (int i=0; i<alphas.length(); ++i) {
042            bf[alphas.charAt(i)] = 3;
043        }
044        alphas = alphas.toUpperCase();
045        for (int i=0; i<alphas.length(); ++i) {
046            bf[alphas.charAt(i)] = 3;
047        }
048        String digits = "1234567890";
049        for (int i=0; i<digits.length(); ++i) {
050            bf[digits.charAt(i)] = 2;
051        }
052        String scheme = "+-.";
053        for (int i=0; i<scheme.length(); ++i) {
054            bf[scheme.charAt(i)] = 2;
055        }
056    }
057
058    /**
059     * Returns true if the start of the array is a valid scheme.
060     * @param bytes byte array in which to look for a valid scheme at the beginning
061     * @return true if the start of the array is a valid scheme
062     */
063    public static boolean startsWithScheme(byte[] bytes) {
064        boolean result = false;
065        if (bytes != null) {
066            int idx = 0;
067            boolean bLoop = true;
068            int c;
069            while (bLoop) {
070                if (idx < bytes.length) {
071                    c = bytes[idx];
072                    if (c == ':') {
073                        bLoop = false;
074                        if (idx > 0) {
075                            result = true;
076                        }
077                    } else if (idx > 0) {
078                        bLoop = ((bf[c & 255] & 2) != 0);
079                    } else {
080                        bLoop = ((bf[c & 255] & 1) != 0);
081                    }
082                } else {
083                    bLoop = false;
084                }
085                ++idx;
086            }
087        }
088        return result;
089    }
090
091    /**
092     * Returns a scheme if the given string has a valid scheme at the beginning
093     * of null if no scheme was found.
094     * @param uri a possible URI string
095     * @return a scheme if one is found or null
096     */
097    public static String getScheme(String uri) {
098        if (uri != null) {
099            StringBuilder sb = new StringBuilder();
100            int idx = 0;
101            boolean bLoop = true;
102            int c;
103            while (bLoop) {
104                if (idx < uri.length()) {
105                    c = uri.charAt(idx);
106                    if (c < 256) {
107                        if (c == ':') {
108                            //bLoop = false;
109                            if (idx > 0) {
110                                return sb.toString();
111                            }
112                        } else if (idx > 0) {
113                            sb.append((char)c);
114                            bLoop = ((bf[c] & 2) != 0);
115                        } else {
116                            sb.append((char)c);
117                            bLoop = ((bf[c] & 1) != 0);
118                        }
119                    } else {
120                        bLoop = false;
121                    }
122                } else {
123                    bLoop = false;
124                }
125                ++idx;
126            }
127        }
128        return null;
129    }
130
131}