/** * Copyright (C) 2014-2017 Philip Helger (www.helger.com) * philip[at]helger[dot]com * * 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.helger.css.supplementary.parser; import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import com.helger.commons.text.codepoint.Codepoint; public final class CSSCodepoint extends Codepoint { private final int m_nCharCount; // Lazily initialized private ECSSTokenStartType m_eTokenStartType; /** * Private constructor for EOF. */ private CSSCodepoint () { // -1 is an invalid codepoint! super (-1, true); m_nCharCount = 1; m_eTokenStartType = ECSSTokenStartType.EOF; } public CSSCodepoint (@Nonnegative final int nValue) { super (nValue); m_nCharCount = 1; } public CSSCodepoint (@Nonnegative final char cHigh, @Nonnegative final char cLow) { super (cHigh, cLow); m_nCharCount = 2; } public boolean isSingleChar () { return m_nCharCount == 1; } public boolean isEOF () { return getValue () == -1; } private static boolean inRange (final int nCodepoint, final int nLow, final int nHigh) { return nCodepoint >= nLow && nCodepoint <= nHigh; } @Override public boolean isDigit () { return inRange (getValue (), '0', '9'); } public boolean isHexDigit () { final int nValue = getValue (); return isDigit () || inRange (nValue, 'A', 'F') || inRange (nValue, 'a', 'f'); } public boolean isUppercaseLetter () { return inRange (getValue (), 'A', 'Z'); } public boolean isLowercaseLetter () { return inRange (getValue (), 'a', 'z'); } public boolean isLetter () { return isUppercaseLetter () || isLowercaseLetter (); } public boolean isNonASCII () { return getValue () >= 0x80; } public boolean isNameStart () { return isLetter () || isNonASCII () || getValue () == '_'; } public boolean isName () { return isNameStart () || isDigit () || getValue () == '-'; } public boolean isNonPrintable () { final int nValue = getValue (); return inRange (nValue, 0, 8) || nValue == '\t' || inRange (nValue, 0x0e, 0x1f); } public boolean isNewline () { return getValue () == '\n'; } public boolean isWhitespace () { final int nValue = getValue (); return isNewline () || nValue == '\t' || nValue == ' '; } @Override public void appendTo (@Nonnull final StringBuilder aSB) { final int nValue = getValue (); if (isSingleChar ()) { aSB.append ((char) nValue); } else { aSB.append (Character.highSurrogate (nValue)); aSB.append (Character.lowSurrogate (nValue)); } } @Nonnull private ECSSTokenStartType _findTokenStartType () { final int nValue = getValue (); switch (nValue) { // \r and \f is handled by the CSSInputStream! case '\n': case '\t': case ' ': return ECSSTokenStartType.WHITESPACE; case '"': return ECSSTokenStartType.QUOTATION_MARK; case '#': return ECSSTokenStartType.NUMBER_SIGN; case '$': return ECSSTokenStartType.DOLLAR_SIGN; case '\'': return ECSSTokenStartType.APOSTROPHE; case '(': return ECSSTokenStartType.LEFT_PARENTHESIS; case ')': return ECSSTokenStartType.RIGHT_PARENTHESIS; case '*': return ECSSTokenStartType.ASTERISK; case '+': return ECSSTokenStartType.PLUS_SIGN; case ',': return ECSSTokenStartType.COMMA; case '-': return ECSSTokenStartType.HYPHEN_MINUS; case '.': return ECSSTokenStartType.FULL_STOP; case '/': return ECSSTokenStartType.SOLIDUS; case ':': return ECSSTokenStartType.COLON; case ';': return ECSSTokenStartType.SEMICOLON; case '<': return ECSSTokenStartType.LESS_THAN_SIGN; case '@': return ECSSTokenStartType.COMMERCIAL_AT; case '[': return ECSSTokenStartType.LEFT_SQUARE_BRACKET; case '\\': return ECSSTokenStartType.REVERSE_SOLIDUS; case ']': return ECSSTokenStartType.RIGHT_SQUARE_BRACKET; case '^': return ECSSTokenStartType.CIRCUMFLEX_ACCENT; case '{': return ECSSTokenStartType.LEFT_CURLY_BRACKET; case '}': return ECSSTokenStartType.RIGHT_CURLY_BRACKET; case '|': return ECSSTokenStartType.VERTICAL_LINE; case '~': return ECSSTokenStartType.TILDE; case -1: return ECSSTokenStartType.EOF; } if (nValue >= '0' && nValue <= '9') return ECSSTokenStartType.DIGIT; if ((nValue >= 'a' && nValue <= 'z') || (nValue >= 'A' && nValue <= 'Z') || nValue == '_' || nValue > 0x80) return ECSSTokenStartType.NAME_START; return ECSSTokenStartType.ANYTHING_ELSE; } @Nonnull public ECSSTokenStartType getTokenStartType () { ECSSTokenStartType ret = m_eTokenStartType; if (ret == null) { ret = _findTokenStartType (); m_eTokenStartType = ret; } return ret; } @Nonnull public static CSSCodepoint createEOF () { return new CSSCodepoint (); } }