All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.phloc.css.parser.ParseUtils Maven / Gradle / Ivy

There is a newer version: 3.7.7
Show newest version
/**
 * Copyright (C) 2006-2014 phloc systems
 * http://www.phloc.com
 * office[at]phloc[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.phloc.css.parser;

import java.util.regex.Matcher;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.RegEx;
import javax.annotation.concurrent.Immutable;

import com.phloc.commons.annotations.PresentForCodeCoverage;
import com.phloc.commons.regex.RegExHelper;
import com.phloc.commons.string.StringHelper;
import com.phloc.css.propertyvalue.CCSSValue;

/**
 * This class is used by the generated parsers to do some common stuff.
 *
 * @author Philip Helger
 */
@Immutable
public final class ParseUtils
{
  /** The character used to quote elements in CSS URLs */
  public static final char URL_ESCAPE_CHAR = '\\';

  // Order of the rules in brackets is important!
  @RegEx
  private static final String SPLIT_NUMBER_REGEX = "^([0-9]*\\.[0-9]+|[0-9]+).*$";

  @PresentForCodeCoverage
  @SuppressWarnings ("unused")
  private static final ParseUtils s_aInstance = new ParseUtils ();

  private ParseUtils ()
  {}

  @Nonnull
  private static String _trimBy (@Nonnull final CharSequence s, final int nLeftSkip, final int nRightSkip)
  {
    return s.toString ().substring (nLeftSkip, s.length () - nRightSkip);
  }

  /**
   * Remove surrounding quotes (single or double) of a string (if present). If
   * the start and the end quote are not equal, nothing happens.
   *
   * @param sStr
   *        The string where the quotes should be removed
   * @return The string without quotes.
   */
  @Nullable
  public static String extractStringValue (@Nullable final String sStr)
  {
    if (StringHelper.hasNoText (sStr) || sStr.length () < 2)
      return sStr;

    final char cFirst = sStr.charAt (0);
    if ((cFirst == '"' || cFirst == '\'') && StringHelper.getLastChar (sStr) == cFirst)
    {
      // Remove quotes around the string
      return _trimBy (sStr, 1, 1);
    }
    return sStr;
  }

  /**
   * Unescape all escaped characters in a CSS URL. All characters masked with a
   * '\\' character replaced.
   *
   * @param sEscapedURL
   *        The escaped URL. May not be null!
   * @return The unescaped URL or the original string, if not a single escape
   *         sequence is found.
   */
  @Nonnull
  public static String unescapeURL (@Nonnull final String sEscapedURL)
  {
    int nIndex = sEscapedURL.indexOf (URL_ESCAPE_CHAR);
    if (nIndex < 0)
    {
      // No escape sequence found
      return sEscapedURL;
    }

    final StringBuilder aSB = new StringBuilder (sEscapedURL.length ());
    int nPrevIndex = 0;
    do
    {
      // Append everything before the first quote char
      aSB.append (sEscapedURL, nPrevIndex, nIndex);
      // Append the quoted char itself
      aSB.append (sEscapedURL, nIndex + 1, nIndex + 2);
      // The new position to start searching
      nPrevIndex = nIndex + 2;
      // Search the next escaped char
      nIndex = sEscapedURL.indexOf (URL_ESCAPE_CHAR, nPrevIndex);
    } while (nIndex >= 0);
    // Append the rest
    aSB.append (sEscapedURL.substring (nPrevIndex));
    return aSB.toString ();
  }

  /**
   * Remove the leading "url(" and the trailing ")" from an URL CSS value. No
   * check is performed for the existence of a leading "url("! This method
   * should only be called from within the parser.
   *
   * @param s
   *        The value to remove the string from.
   * @return The trimmed value. Never null.
   */
  @Nonnull
  public static String trimUrl (@Nonnull final CharSequence s)
  {
    // Extract from "url(...)"
    final String sTrimmed = _trimBy (s, CCSSValue.PREFIX_URL_OPEN.length (), CCSSValue.SUFFIX_URL_CLOSE.length ()).trim ();
    // Remove the trailing quotes (if any)
    final String sUnquoted = extractStringValue (sTrimmed);
    // Unescape all escaped chars
    return unescapeURL (sUnquoted);
  }

  @Nonnull
  public static String splitNumber (@Nonnull final StringBuilder aPattern)
  {
    // Find the longest matching number within the pattern
    final Matcher m = RegExHelper.getMatcher (SPLIT_NUMBER_REGEX, aPattern.toString ());
    if (m.matches ())
      return m.group (1);
    return "";
  }

  /**
   * In CSS, identifiers (including element names, classes, and IDs in
   * selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646
   * characters U+00A0 and higher, plus the hyphen (-) and the underscore (_);
   * they cannot start with a digit, two hyphens, or a hyphen followed by a
   * digit. Identifiers can also contain escaped characters and any ISO 10646
   * character as a numeric code (see next item). For instance, the identifier
   * "B&W?" may be written as "B\&W\?" or "B\26 W\3F".
   *
   * @param aPattern
   *        pattern to check
   * @return The input string
   */
  @Nonnull
  public static String validateIdentifier (@Nonnull final StringBuilder aPattern)
  {
    final int nLength = aPattern.length ();
    final char c1 = aPattern.charAt (0);
    final char c2 = nLength <= 1 ? 0 : aPattern.charAt (1);

    // Starts with a hack?
    if (c1 == '-' || c1 == '$' || c1 == '*')
    {
      if (nLength > 1 && Character.isDigit (c2))
        throw new TokenMgrError ("Identifier may not start with a hyphen and a digit: " + aPattern,
                                 TokenMgrError.LEXICAL_ERROR);
    }
    else
    {
      if (Character.isDigit (c1))
        throw new TokenMgrError ("Identifier may not start with a digit: " + aPattern, TokenMgrError.LEXICAL_ERROR);
    }

    if (nLength > 1 && c1 == '-' && c2 == '-')
      throw new TokenMgrError ("Identifier may not start with two hyphens: " + aPattern, TokenMgrError.LEXICAL_ERROR);

    return aPattern.toString ();
  }

  /**
   * Unescape e.g. \26 or \000026 to
   * &.
   *
   * @param aImage
   *        Source string
   * @return Unmasked string
   */
  @Nonnull
  public static String unescapeUnicode (final StringBuilder aImage)
  {
    // FIXME
    return aImage.toString ();
  }

  /**
   * Unescape e.g. \x to x.
   *
   * @param aImage
   *        Source string
   * @return Unmasked string
   */
  @Nonnull
  public static String unescapeOther (final StringBuilder aImage)
  {
    // FIXME
    return aImage.toString ();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy