001    /*
002     * Copyright (c) 2006 Henri Sivonen
003     *
004     * Permission is hereby granted, free of charge, to any person obtaining a 
005     * copy of this software and associated documentation files (the "Software"), 
006     * to deal in the Software without restriction, including without limitation 
007     * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
008     * and/or sell copies of the Software, and to permit persons to whom the 
009     * Software is furnished to do so, subject to the following conditions:
010     *
011     * The above copyright notice and this permission notice shall be included in 
012     * all copies or substantial portions of the Software.
013     *
014     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
015     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
016     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
017     * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
018     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
019     * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
020     * DEALINGS IN THE SOFTWARE.
021     */
022    
023    package fi.iki.hsivonen.xml;
024    
025    import java.util.LinkedList;
026    import java.util.List;
027    import java.util.regex.Matcher;
028    import java.util.regex.Pattern;
029    
030    /**
031     * Static utilities for working with (X)HTML5 attribute values.
032     * 
033     * @version $Id: AttributeUtil.java,v 1.4 2006/11/29 18:13:33 hsivonen Exp $
034     * @author hsivonen
035     */
036    public class AttributeUtil {
037    
038        /**
039         * An empty string array instance.
040         */
041        private final static String[] EMPTY_STRING_ARRAY = {};
042    
043        /**
044         * The pattern for extracting an integer by skipping white space and ignoring 
045         * trailing garbage.
046         */
047        private static Pattern INTEGER_PATTERN = Pattern.compile("^[ \t\n\r]*(-?[0-9]+)");
048    
049        /**
050         * Private constructor to prevent instantiation.
051         */
052        private AttributeUtil() {
053            super();
054        }
055    
056        /**
057         * Returns the integer represented by <code>attrVal</code> or 
058         * <code>Integer.MIN_VALUE</code> on error.
059         * 
060         * @param attrVal a string representing an integer attribute 
061         * value (can be <code>null</code>)
062         * @return the integer represented by <code>attrVal</code> or 
063         * <code>Integer.MIN_VALUE</code> on error
064         */
065        public static int parseInteger(String attrVal) {
066            if (attrVal == null) {
067                return Integer.MIN_VALUE;
068            }
069            Matcher m = INTEGER_PATTERN.matcher(attrVal);
070            if (!m.matches()) {
071                return Integer.MIN_VALUE;
072            }
073            try {
074                return Integer.parseInt(m.group(1));
075            } catch (NumberFormatException e) {
076                return Integer.MIN_VALUE;
077            }
078        }
079    
080        /**
081         * Returns the non-negative integer represented by 
082         * <code>attrVal</code> or -1 on error.
083         * 
084         * @param attrVal a string representing a non-negative 
085         * integer attribute value (can be <code>null</code>)
086         * @return the integer represented by <code>attrVal</code> or 
087         * -1 on error
088         */
089        public static int parseNonNegativeInteger(String attrVal) {
090            int rv = parseInteger(attrVal);
091            if (rv < 0) {
092                return -1;
093            } else {
094                return rv;
095            }
096        }
097    
098        /**
099         * Returns the positive integer represented by 
100         * <code>attrVal</code> or -1 on error.
101         * 
102         * @param attrVal a string representing a positive 
103         * integer attribute value (can be <code>null</code>)
104         * @return the integer represented by <code>attrVal</code> or 
105         * -1 on error
106         */
107        public static int parsePositiveInteger(String attrVal) {
108            int rv = parseInteger(attrVal);
109            if (rv < 1) {
110                return -1;
111            } else {
112                return rv;
113            }
114        }
115    
116        /**
117         * Splits the argument on white space.
118         * 
119         * @param value the attribute value
120         * @return a string array with zero or more strings none of which 
121         * is the empty string and none of which contains white space characters.
122         */
123        public static String[] split(String value) {
124            if (value == null || "".equals(value)) {
125                return EMPTY_STRING_ARRAY;
126            }
127            int len = value.length();
128            List<String> list = new LinkedList<String>();
129            boolean collectingSpace = true;
130            int start = 0;
131            for (int i = 0; i < len; i++) {
132                char c = value.charAt(i);
133                if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
134                    if (!collectingSpace) {
135                        list.add(value.substring(start, i));
136                        collectingSpace = true;
137                    }
138                } else {
139                    if (collectingSpace) {
140                        start = i;
141                        collectingSpace = false;
142                    }
143                }
144            }
145            if (start < len) {
146                list.add(value.substring(start, len));
147            }
148            return list.toArray(EMPTY_STRING_ARRAY);
149        }
150    
151    }