Package org.jboss.as.console.client.core

Source Code of org.jboss.as.console.client.core.NewTokenFormatter

package org.jboss.as.console.client.core;

import com.google.gwt.http.client.URL;
import com.google.inject.Inject;
import com.gwtplatform.mvp.shared.proxy.PlaceRequest;
import com.gwtplatform.mvp.shared.proxy.TokenFormatException;
import com.gwtplatform.mvp.shared.proxy.TokenFormatter;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
* Formats tokens from {@code String} values to {@link PlaceRequest} and {@link PlaceRequest} hierarchies and vice-versa. The default implementation
* uses:
* <ul>
* <li>{@code '/'} to separate {@link PlaceRequest}s in a hierarchy;</li>
* <li>{@code ';'} to separate parameters in a {@link PlaceRequest};</li>
* <li>{@code '='} to separate the parameter name from its value;</li>
* <li>{@code '\'} to escape separators inside parameters names and values in a {@link PlaceRequest}.</li>
* </ul>
* These symbols cannot be used in a name token. If one of the separating symbol is encountered in a parameter or a value it is escaped using the
* {@code '\'} character by replacing {@code '/'} with {@code '\0'}, {@code ';'} with {@code '\1'}, {@code '='} with {@code '\2'} and {@code '\'} with
* {@code '\3'}.
* <p />
* Before decoding a {@link String} URL fragment into a {@link PlaceRequest} or a {@link PlaceRequest} hierarchy, {@link NewTokenFormatter} will first
* pass the {@link String} through {@link URL#decodeQueryString(String)} so that if the URL was URL-encoded by some user agent, like a mail user
* agent, it is still parsed correctly.
* <p />
* For example, {@link NewTokenFormatter} would parse any of the following:
*
* <pre>
* nameToken1%3Bparam1.1%3Dvalue1.1%3Bparam1.2%3Dvalue1.2%2FnameToken2%2FnameToken3%3Bparam3.1%3Dvalue%03%11
* nameToken1;param1.1=value1.1;param1.2=value1.2/nameToken2/nameToken3;param3.1=value\03\21
* </pre>
*
* Into the following hierarchy of {@link PlaceRequest}:
*
* <pre>
* {
*   { "nameToken1", { {"param1.1", "value1.1"}, {"parame1.2","value1.2"} },
*     "nameToken2", {},
*     "nameToken3", { {"param3.1", "value/3=1"} } }
* }
* </pre>
*
* If you want to use different symbols as separator, use the {@link #ParameterTokenFormatter(String, String, String, String)} constructor.
*
* @author Philippe Beaudoin
* @author Yannis Gonianakis
* @author Daniel Colchete
*/
public class NewTokenFormatter implements TokenFormatter {

  protected static final String DEFAULT_HIERARCHY_SEPARATOR = "/";
  protected static final String DEFAULT_PARAM_SEPARATOR = ";";
  protected static final String DEFAULT_VALUE_SEPARATOR = "=";
  protected static final String DEFAULT_ESCAPE_CHARACTER = "\\";

  protected static final String ESCAPED_HIERARCHY_SEPARATOR = "\\0"; // escaped version for the DEFAULT_HIERARCHY_SEPARATOR
  protected static final String ESCAPED_PARAM_SEPARATOR = "\\1"; // escaped version for the DEFAULT_PARAM_SEPARATOR
  protected static final String ESCAPED_VALUE_SEPARATOR = "\\2"; // escaped version for the DEFAULT_VALUE_SEPARATOR
  protected static final String ESCAPED_ESCAPE_CHAR = "\\3"; // escaped version for the DEFAULT_ESCAPE_CHARACTER

  private final String escapeCharacter;
  private final String hierarchySeparator;
  private final String paramSeparator;
  private final String valueSeparator;

  /**
   * Builds a {@link NewTokenFormatter} using the default separators and escape character.
   */
  @Inject
  public NewTokenFormatter() {
    this(DEFAULT_HIERARCHY_SEPARATOR, DEFAULT_PARAM_SEPARATOR, DEFAULT_VALUE_SEPARATOR, DEFAULT_ESCAPE_CHARACTER);
  }

  /**
   * This constructor makes it possible to use custom separators in your token formatter. The separators must be 1-letter strings, they must all be
   * different from one another, and they must be encoded when ran through {@link URL#encodeQueryString(String)})
   *
   * @param hierarchySeparator
   *            The symbol used to separate {@link PlaceRequest} in a hierarchy. Must be a 1-character string and can't be {@code %}.
   * @param paramSeparator
   *            The symbol used to separate parameters in a {@link PlaceRequest}. Must be a 1-character string and can't be {@code %}.
   * @param valueSeparator
   *            The symbol used to separate the parameter name from its value. Must be a 1-character string and can't be {@code %}.
   */
  public NewTokenFormatter(String hierarchySeparator, String paramSeparator, String valueSeparator) {
    this(hierarchySeparator, paramSeparator, valueSeparator, DEFAULT_ESCAPE_CHARACTER);
  }

  /**
   * This constructor makes it possible to use custom separators and custom escape character in your token formatter. The separators and the escape
   * character must be 1-letter strings, they must all be different from one another, and they must be encoded when ran through
   * {@link URL#encodeQueryString(String)})
   *
   * @param hierarchySeparator
   *            The symbol used to separate {@link PlaceRequest} in a hierarchy. Must be a 1-character string and can't be {@code %}.
   * @param paramSeparator
   *            The symbol used to separate parameters in a {@link PlaceRequest}. Must be a 1-character string and can't be {@code %}.
   * @param valueSeparator
   *            The symbol used to separate the parameter name from its value. Must be a 1-character string and can't be {@code %}.
   * @param escapeCharacter
   *            The symbol used to escape the separator symbols inside parameter names and values. Must be a 1-character string and can't be
   *            {@code %}.
   */
  public NewTokenFormatter(String hierarchySeparator, String paramSeparator, String valueSeparator, String escapeCharacter) {
    assert hierarchySeparator.length() == 1;
    assert paramSeparator.length() == 1;
    assert valueSeparator.length() == 1;
    assert escapeCharacter.length() == 1;
    assert !escapeCharacter.equals(hierarchySeparator);
    assert !escapeCharacter.equals(paramSeparator);
    assert !escapeCharacter.equals(valueSeparator);
    assert !hierarchySeparator.equals(paramSeparator);
    assert !hierarchySeparator.equals(valueSeparator);
    assert !paramSeparator.equals(valueSeparator);
    assert !escapeCharacter.equals(URL.encodeQueryString(escapeCharacter));
    assert !valueSeparator.equals(URL.encodeQueryString(valueSeparator));
    assert !hierarchySeparator.equals(URL.encodeQueryString(hierarchySeparator));
    assert !paramSeparator.equals(URL.encodeQueryString(paramSeparator));
    assert !escapeCharacter.equals("%");
    assert !hierarchySeparator.equals("%");
    assert !paramSeparator.equals("%");
    assert !valueSeparator.equals("%");

    this.hierarchySeparator = hierarchySeparator;
    this.paramSeparator = paramSeparator;
    this.valueSeparator = valueSeparator;
    this.escapeCharacter = escapeCharacter;
  }

  @Override
  public String toHistoryToken(List<PlaceRequest> placeRequestHierarchy) throws TokenFormatException {
    StringBuilder out = new StringBuilder();

    for (int i = 0; i < placeRequestHierarchy.size(); ++i) {
      if (i != 0) {
        out.append(hierarchySeparator);
      }
      out.append(toPlaceTokenUnescaped(placeRequestHierarchy.get(i)));
    }

    return out.toString();
  }

  @Override
  public PlaceRequest toPlaceRequest(String placeToken) throws TokenFormatException {
    return unescapedToPlaceRequest(URL.decodeQueryString(placeToken));
  }

  private PlaceRequest unescapedToPlaceRequest(String unescapedPlaceToken) throws TokenFormatException {
    PlaceRequest req = null;

    int split = unescapedPlaceToken.indexOf(paramSeparator);
    if (split == 0) {
      throw new TokenFormatException("Place history token is missing.");
    } else if (split == -1) {
      req = new PlaceRequest(unescapedPlaceToken);
    } else if (split >= 0) {
      req = new PlaceRequest(unescapedPlaceToken.substring(0, split));
      String paramsChunk = unescapedPlaceToken.substring(split + 1);
      String[] paramTokens = paramsChunk.split(paramSeparator);
      for (String paramToken : paramTokens) {
        if (paramToken.isEmpty()) {
          throw new TokenFormatException("Bad parameter: Successive parameters require a single '" + paramSeparator + "' between them.");
        }
        String[] param = splitParamToken(paramToken);
        String key = paramValueUnescape(param[0]);
        String value = paramValueUnescape(param[1]);
        req = req.with(key, value);
      }
    }
    return req;
  }

  @Override
  public List<PlaceRequest> toPlaceRequestHierarchy(String historyToken) throws TokenFormatException {
    historyToken = URL.decodeQueryString(historyToken);

    int split = historyToken.indexOf(hierarchySeparator);
    if (split == 0) {
      throw new TokenFormatException("Place history token is missing.");
    } else {
      List<PlaceRequest> result = new ArrayList<PlaceRequest>();
      if (split == -1) {
        result.add(unescapedToPlaceRequest(historyToken)); // History token consists of a single place token
      } else {
        String[] placeTokens = historyToken.split(hierarchySeparator);
        for (String placeToken : placeTokens) {
          if (placeToken.isEmpty()) {
            throw new TokenFormatException("Bad parameter: Successive place tokens require a single '" + hierarchySeparator + "' between them.");
          }
          result.add(unescapedToPlaceRequest(placeToken));
        }
      }
      return result;
    }
  }
 
  @Override
  public String toPlaceToken(PlaceRequest placeRequest) throws TokenFormatException {
    return toPlaceTokenUnescaped(placeRequest);
  }

  private String toPlaceTokenUnescaped(PlaceRequest placeRequest) throws TokenFormatException {
    StringBuilder out = new StringBuilder();
    out.append(placeRequest.getNameToken());

    Set<String> params = placeRequest.getParameterNames();
    if (params != null) {
      for (String name : params) {
        out.append(paramSeparator).append(paramValueEscape(name)).append(valueSeparator).append(paramValueEscape(placeRequest.getParameter(name, null)));
      }
    }
   
    return out.toString();
  }

  private String[] splitParamToken(String paramToken) {
    String[] param = paramToken.split(valueSeparator, 2);
    if (param.length == 1 // pattern didn't match
        || param[0].contains(valueSeparator) // un-escaped separator encountered in the key
        || param[1].contains(valueSeparator)) { // un-escaped separator encountered in the value
      throw new TokenFormatException("Bad parameter: Parameters require a single '" + valueSeparator + "' between the key and value.");
    }
    return param;
  }

  private String paramValueEscape(String value) {

    StringBuffer sbuf = new StringBuffer();
    int len = value.length();

    char escapeChar = escapeCharacter.charAt(0);
    char hierarchyChar = hierarchySeparator.charAt(0);
    char paramChar = paramSeparator.charAt(0);
    char valueChar = valueSeparator.charAt(0);

    for (int i = 0; i < len; i++) {
      char ch = value.charAt(i);

      if (ch == escapeChar) {
        sbuf.append(ESCAPED_ESCAPE_CHAR);
      } else if (ch == hierarchyChar) {
        sbuf.append(ESCAPED_HIERARCHY_SEPARATOR);
      } else if (ch == paramChar) {
        sbuf.append(ESCAPED_PARAM_SEPARATOR);
      } else if (ch == valueChar) {
        sbuf.append(ESCAPED_VALUE_SEPARATOR);
      } else {
        sbuf.append(ch);
      }
    }

    return URL.encodeQueryString(sbuf.toString());
  }

  private String paramValueUnescape(String value) {
    value = URL.decodeQueryString(value);
   
    StringBuffer sbuf = new StringBuffer();
    int len = value.length();

    char escapeChar = escapeCharacter.charAt(0);

    char hierarchyNum = ESCAPED_HIERARCHY_SEPARATOR.charAt(1);
    char paramNum = ESCAPED_PARAM_SEPARATOR.charAt(1);
    char valueNum = ESCAPED_VALUE_SEPARATOR.charAt(1);
    char escapeNum = ESCAPED_ESCAPE_CHAR.charAt(1);

    for (int i = 0; i < len; i++) {
      char ch = value.charAt(i);

      if (ch == escapeChar) {
        i++;
        char ch2 = value.charAt(i);
        if (ch2 == hierarchyNum) {
          sbuf.append(hierarchySeparator);
        } else if (ch2 == paramNum) {
          sbuf.append(paramSeparator);
        } else if (ch2 == valueNum) {
          sbuf.append(valueSeparator);
        } else if (ch2 == escapeNum) {
          sbuf.append('\\');
        }
      } else {
        sbuf.append(ch);
      }
    }  

    return sbuf.toString();
  }

}
TOP

Related Classes of org.jboss.as.console.client.core.NewTokenFormatter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.