text:
package org.simpleframework.http.parse;

import org.simpleframework.http.Address;
import org.simpleframework.http.Path;
import org.simpleframework.http.Query;
import org.simpleframework.util.KeyMap;
import org.simpleframework.util.parse.Parser;

/**
 * This parser is used to parse uniform resource identifiers.
 * The uniform resource identifier syntax is given in RFC 2396.
 * This parser can parse relative and absolute URI's. The
 * uniform resource identifier syntax that this parser will
 * parse are based on the generic web based URL similar to
 * the syntax represented in RFC 2616 section 3.2.2. The syntax
 * used to parse this URI is a modified version of RFC 2396
 *

 *
 *    URI         = (absoluteURI | relativeURI)
 *    absoluteURI = scheme ":" ("//" netpath | relativeURI)
 *    relativeURI = path ["?" querypart]
 *    netpath     = domain [":" port] relativeURI
 *    path        = *("/" segment)
 *    segment     = *pchar *( ";" param )
 *
 *

 * This implements the Address interface and provides
 * methods that access the various parts of the URI. The parameters
 * in the path segments of the uniform resource identifier are
 * stored in name value pairs. If parameter names are not unique
 * across the path segments then only the deepest parameter will be
 * stored from the path segment. For example if the URI represented
 * was http://domain/path1;x=y/path2;x=z the value for
 * the parameter named x would be z.
 *

 * This will normalize the path part of the uniform resource
 * identifier. A normalized path is one that contains no back
 * references like "./" and "../". The normalized path will not
 * contain the path parameters.
 *

 * The setPath method is used to reset the path this
 * uniform resource identifier has, it also resets the parameters.
 * The parameters are extracted from the new path given.
 *
 * @author Niall Gallagher
 */
public class AddressParser extends Parser implements Address {

   /**
    * Parameters are stored so that the can be viewed.
    */
   private ParameterMap param;

   /**
    * This is the path used to represent the address path.
    */
   private Path normal;

   /**
    * This contains the query parameters for the address.
    */
   private Query data;

   /**
    * Used to track the characters that form the path.
    */
   private Token path;

   /**
    * Used to track the characters that form the domain.
    */
   private Token domain;

   /**
    * Used to track the characters that form the query.
    */
   private Token query;

   /**
    * Used to track the name characters of a parameter.
    */
   private Token name;

   /**
    * Used to track the value characters of a parameter.
    */
   private Token value;

   /**
    * References the scheme that this URI contains.
    */
   private Token scheme;

   /**
    * Contains the port number if it was specified.
    */
   private int port;

   /**
    * Default constructor will create a AddressParser
    * that contains no specifics. The instance will return
    * null for all the get methods. The parsers
    * get methods are populated by using the parse
    * method.
    */
   public AddressParser(){
      this.param = new ParameterMap();
      this.path = new Token();
      this.domain = new Token();
      this.query = new Token();
      this.scheme = new Token();
      this.name = new Token();
      this.value = new Token();
   }

   /**
    * This is primarily a convenience constructor. This will parse
    * the String given to extract the specifics. This
    * could be achieved by calling the default no-arg constructor
    * and then using the instance to invoke the parse
    * method on that String to extract the parts.
    *
    * @param text a String containing a URI value
    */
   public AddressParser(String text){
      this();
      parse(text);
   }

   /**
    * This allows the scheme of the URL given to be returned.
    * If the URI does not contain a scheme then this will
    * return null. The scheme of the URI is the part that
    * specifies the type of protocol that the URI is used
    * for, an example gopher://domain/path is
    * a URI that is intended for the gopher protocol. The
    * scheme is the string gopher.
    *
    * @return this returns the scheme tag for the URI if
    * there is one specified for it
    */
   public String getScheme(){
      return scheme.toString();
   }

   /**
    * This is used to retrieve the domain of this URI. The
    * domain part in the URI is an optional part, an example
    * http://domain/path?querypart. This will
    * return the value of the domain part. If there is no
    * domain part then this will return null otherwise the
    * domain value found in the uniform resource identifier.
    *
    * @return the domain part of this uniform resource
    * identifier this represents
    */
   public String getDomain(){
      return domain.toString();
   }

   /**
    * This is used to retrieve the path of this URI. The path part
    * is the most fundamental part of the URI. This will return
    * the value of the path. If there is no path part then this
    * will return / to indicate the root.
    *

    * The Path object returned by this will contain
    * no path parameters. The path parameters are available using
    * the Address methods. The reason that this does not
    * contain any of the path parameters is so that if the path is
    * needed to be converted into an OS specific path then the path
    * parameters will not need to be separately parsed out.
    *
    * @return the path that this URI contains, this value will not
    * contain any back references such as "./" and "../" or any
    * path parameters
    */
   public Path getPath(){
      if(normal == null) {
         String text = path.toString();

         if(text == null) {
            normal = new PathParser("/");
         }
         if(normal == null){
            normal = new PathParser(text);
         }
      }
      return normal;
   }

   /**
    * This is used to retrieve the query of this URI. The query part
    * in the URI is an optional part. This will return the value
    * of the query part. If there is no query part then this will
    * return an empty Query object. The query is
    * an optional member of a URI and comes after the path part, it
    * is preceded by a question mark, ? character.
    * For example the following URI contains query for
    * its query part, http://host:port/path?query.
    *

    * This returns a org.simpleframework.http.Query
    * object that can be used to interact directly with the query
    * values. The Query object is a read-only interface
    * to the query parameters, and so will not affect the URI.
    *
    * @return a Query object for the query part
    */
   public Query getQuery(){
      if(data == null) {
         String text = query.toString();

         if(text == null) {
            data = new QueryParser();
         }
         if(data == null){
            data = new QueryParser(text);
         }
      }
      return data;
   }

   /**
    * This is used to retrieve the port of the uniform resource
    * identifier. The port part in this is an optional part, an
    * example http://host:port/path?querypart. This
    * will return the value of the port. If there is no port then
    * this will return -1 because this represents
    * an impossible uniform resource identifier port. The port
    * is an optional part.
    *
    * @return this returns the port of the uniform resource
    * identifier
    */
   public int getPort(){
      return port <= 0? -1 : port;
   }

   /**
    * This extracts the parameter values from the uniform resource
    * identifier represented by this object. The parameters that a
    * uniform resource identifier contains are embedded in the path
    * part of the URI. If the path contains no parameters then this
    * will return an empty Map instance.
    *

    * This will produce unique name and value parameters. Thus if the
    * URI contains several path segments with similar parameter names
    * this will return the deepest parameter. For example if the URI
    * represented was http://domain/path1;x=y/path2;x=z
    * the value for the parameter named x would be
    * z.
    *
    * @return this will return the parameter names found in the URI
    */
   public KeyMap<String> getParameters(){
      return param;
   }

   /**
    * This allows the scheme for the URI to be specified.
    * If the URI does not contain a scheme then this will
    * attach the scheme and the :// identifier
    * to ensure that the Address.toString will
    * produce the correct syntax.
    *

    * Caution must be taken to ensure that the port and
    * the scheme are consistent. So if the original URI
    * was http://domain:80/path and the scheme
    * was changed to ftp the port number that
    * remains is the standard HTTP port not the FTP port.
    *
    * @param value this specifies the protocol this URI
    * is intended for
    */
   public void setScheme(String value){
      scheme.value = value;
   }

   /**
    * This will set the domain to whatever value is in the
    * string parameter. If the string is null then this URI
    * objects toString method will not contain
    * the domain. The result of the toString
    * method will be /path/path?query. If the
    * path is non-null this URI will contain the path.
    *
    * @param value this will be the new domain of this
    * uniform resource identifier, if it is not null
    */
   public void setDomain(String value){
      domain.value = value;
   }

   /**
    * This will set the port to whatever value it is given. If
    * the value is 0 or less then the toString will
    * will not contain the optional port. If port number is above
    * 0 then the toString method will produce a URI
    * like http://host:123/path but only if there is
    * a valid domain.
    *
    * @param port the port value that this URI is to have
    */
   public void setPort(int port) {
      this.port = port;
   }

   /**
    * This will set the path to whatever value it is given. If the
    * value is null then this Address.toString method will
    * not contain the path, that is if path is null then it will be
    * interpreted as /.
    *

    * This will reset the parameters this URI has. If the value
    * given to this method has embedded parameters these will form
    * the parameters of this URI. The value given may not be the
    * same value that the getPath produces. The path
    * will have all back references and parameters stripped.
    *
    * @param text the path that this URI is to be set with
    */
   public void setPath(String text) {
      if(!text.startsWith("/")){
         text = "/" + text;
      }
      domain.toString();
      query.toString();
      scheme.toString();
      param.clear();
      path.clear();
      parsePath(text); /*extract params*/
   }

   /**
    * This will set the path to whatever value it is given. If the
    * value is null then this Address.toString method
    * will not contain the path, that is if path is null then it will
    * be interpreted as /.
    *

    * This will reset the parameters this URI has. If the value
    * given to this method has embedded parameters these will form
    * the parameters of this URI. The value given may not be the
    * same value that the getPath produces. The path
    * will have all back references and parameters stripped.
    *
    * @param path the path that this URI is to be set with
    */
   public void setPath(Path path) {
      if(path != null){
         normal = path;
      }else {
         setPath("/");
      }
   }

   /**
    * This is used to parse the path given with the setPath
    * method. The path contains name and value pairs. These parameters
    * are embedded into the path segments using a semicolon character,
    * ';'. Since the parameters to not form part of the actual path
    * mapping they are removed from the path and stored. Each parameter
    * can then be extracted from this parser using the methods provided
    * by the Address interface.
    *
    * @param path this is the path that is to be parsed and have the
    * parameter values extracted
    */
   private void parsePath(String path){
      count = path.length();
      ensureCapacity(count);
      path.getChars(0, count, buf, 0);
      normal = null;
      off = 0;
      path();
   }

   /**
    * This will set the query to whatever value it is given. If the
    * value is null then this Address.toString method
    * will not contain the query. If the query was abc
    * then the toString method would produce a string
    * like http://host:port/path?abc. If the query is
    * null this URI would have no query part. The query must not
    * contain the ? character.
    *
    * @param value the query that this uniform resource identifier
    * is to be set to if it is non-null
    */
   public void setQuery(String value) {
      query.value = value;
      data = null;
   }

   /**
    * This will set the query to whatever value it is given. If the
    * value is null then this Address.toString method
    * will not contain the query. If the Query.toString
    * returns null then the query will be empty. This is basically
    * the setQuery(String) method with the string value
    * from the issued Query.toString method.
    *
    * @param query a Query object that contains
    * the name value parameters for the query
    */
   public void setQuery(Query query) {
      if(value != null) {
         data = query;
      }else {
         setQuery("");
      }
   }

   /**
    * This will check to see what type of URI this is if it is an
    * absoluteURI or a relativeURI. To
    * see the definition of a URI see RFC 2616 for the definition
    * of a URL and for more specifics see RFC 2396 for the
    * expressions.
    */
   protected void parse(){
      if(count > 0){
         if(buf[0] == '/'){
            relativeURI();
         }else{
            absoluteURI();
         }
      }
   }

   /**
    * This will empty each tokens cache. A tokens cache is used
    * to represent a token once the token's toString
    * method has been called. Thus when the toString
    * method is called then the token depends on the value of the
    * cache alone in further calls to toString.
    * However if a URI has just been parsed and that method has
    * not been invoked then the cache is created from the buf if
    * its length is greater than zero.
    */
   protected void init(){
      param.clear();
      domain.clear();
      path.clear();
      query.clear();
      scheme.clear();
      off =port = 0;
      normal = null;
      data = null;
   }

   /**
    * This is a specific definition of a type of URI. An absolute
    * URI is a URI that contains a host and port. It is the most
    * frequently used type of URI. This will define the host and
    * the optional port part. As well as the relative URI part.
    * This uses a simpler syntax than the one specified in RFC 2396
    *

    *
    *    absoluteURI = scheme ":" ("//" netpath | relativeURI)
    *    relativeURI = path ["?" querypart]
    *    netpath     = domain [":" port] relativeURI
    *    path        = *("/" segment)
    *    segment     = *pchar *( ";" param )
    *
    *

    * This syntax is sufficient to handle HTTP style URI's as well
    * as GOPHER and FTP and various other 'simple' schemes. See
    * RFC 2396 for the syntax of an absoluteURI.
    */
   private void absoluteURI(){
      scheme();
      netPath();
   }

   /**
    * This will check to see if there is a scheme in the URI. If
    * there is a scheme found in the URI this returns true and
    * removes that scheme tag of the form "ftp:" or "http:"
    * or whatever the protocol scheme tag may be for the URI.
    *

    * The syntax for the scheme is given in RFC 2396 as follows
    *

    *
    *    scheme = alpha *( alpha | digit | "+" | "-" | "." )
    *
    *

    * This will however also skips the "://" from the tag
    * so of the URI was gopher://domain/path then
    * the URI would be domain/path afterwards.
    */
   private void scheme(){
      int mark = off;
      int pos = off;

      if(alpha(buf[off])){
         while(off < count){
            char next = buf[off++];

            if(schemeChar(next)){
               pos++;
            }else if(next == ':'){
               if(!skip("//")) {
                  off = mark;
                  pos = mark;
               }
               break;
            }else{
               off = mark;
               pos = mark;
               break;
            }
         }
         scheme.len = pos - mark;
         scheme.off = mark;
      }
   }

   /**
    * This method is used to assist the scheme method. This will
    * check to see if the type of the character is the same as
    * those described in RFC 2396 for a scheme character. The
    * scheme tag can contain an alphanumeric of the following
    * "+", "-", ".".
    *
    * @param c this is the character that is being checked
    *
    * @return this returns true if the character is a valid
    * scheme character
    */
   private boolean schemeChar(char c){
      switch(c){
      case '+': case '-':
      case '.':
         return true;
      default:
         return alphanum(c);
      }
   }

   /**
    * The network path is the path that contains the network
    * address of the host that this URI is targeted at. This
    * will parse the domain name of the host and also a port
    * number before parsing a relativeURI
    *

    *
    *    netpath     = domain [":" port] relativeURI
    *
    *

    * This syntax is modified from the URI specification on
    * RFC 2396.
    */
   private void netPath(){
      domain();
      if(skip(":")){
         port();
      }
      relativeURI();
   }

   /**
    * This is a specific definition of a type of URI. A relative
    * URI is a URI that contains no host or port. It is basically
    * the resource within the host. This will extract the path and
    * the optional query part of the URI. Rfc2396 has the proper
    * definition of a relativeURI.
    */
   private void relativeURI(){
      path();
      if(skip("?")){
         query();
      }
   }

   /**
    * This is used to extract the optional port from a given URI.
    * This will read a sequence of digit characters and convert
    * the String of digit characters into a decimal
    * number. The digits will be added to the port variable. If
    * there is no port number this will not update the read offset.
    */
   private void port() {
      while(off < count){
         if(!digit(buf[off])){
            break;
         }
         port *= 10;
         port += buf[off];
         port -= '0';
         off++;
      }
   }

   /**
    * This is used to extract the domain from the given URI. This
    * will  firstly initialize the token object that represents the
    * domain. This allows the token's toString method to
    * return the extracted value of the token rather than getting
    * confused with previous values set by a previous parse method.
    *

    * This uses the following delimiters to determine the end of the
    * domain ?,: and /. This
    * ensures that the read offset does not go out of bounds and
    * consequently throw an IndexOutOfBoundsException.
    */
   private void domain(){
      int mark = off;

      loop: while(off < count){
         switch(buf[off]){
         case '/': case ':':
         case '?':
            break loop;
         default:
            off++;
         }
      }
      domain.len = off - mark;
      domain.off = mark;
   }

   /**
    * This is used to extract the segments from the given URI. This
    * will firstly initialize the token object that represents the
    * path. This allows the token's toString method to
    * return the extracted value of the token rather than getting
    * confused with previous values set by a previous parse method.
    *

    * This is slightly different from RFC 2396 in that it defines a
    * pchar as the RFC 2396 definition of a pchar without the escaped
    * chars. So this method has to ensure that no escaped chars go
    * unchecked. This ensures that the read offset does not go out
    * of bounds and throw an IndexOutOfBoundsException.
    */
   private void path(){
      int mark = off;
      int pos = off;

      while(skip("/")) {
         buf[pos++] = '/';

         while(off < count){
            if(buf[off]==';'){
               while(skip(";")){
                  param();
                  insert();
               }
               break;
            }
            if(buf[off]=='%'){
               escape();
            }else if(!pchar(buf[off])){
               break;
            }
            buf[pos++]=buf[off++];
         }
      }
      path.len = pos -mark;
      path.off = mark;
   }

   /**
    * This is used to extract the query from the given URI. This
    * will firstly initialize the token object that represents the
    * query. This allows the token's toString method
    * to return the extracted value of the token rather than getting
    * confused with previous values set by a previous parse method.
    * The calculation of the query part of a URI is basically the
    * end of the URI.
    */
   private void query() {
      query.len = count - off;
      query.off = off;
   }

   /**
    * This is an expression that is defined by RFC 2396 it is used
    * in the definition of a segment expression. This is basically
    * a list of pchars.
    *

    * This method has to ensure that no escaped chars go unchecked.
    * This ensures that the read offset does not goe out of bounds
    * and consequently throw an out of bounds exception.
    */
   private void param() {
      name();
      if(skip("=")){ /* in case of error*/
         value();
      }
   }

   /**
    * This extracts the name of the parameter from the character
    * buffer. The name of a parameter is defined as a set of
    * pchars including escape sequences. This will extract the
    * parameter name and buffer the chars. The name ends when a
    * equals character, "=", is encountered or in the case of a
    * malformed parameter when the next character is not a pchar.
    */
   private void name(){
      int mark = off;
      int pos = off;

      while(off < count){
         if(buf[off]=='%'){ /* escaped */
            escape();
         }else if(buf[off]=='=') {
            break;
         }else if(!pchar(buf[off])){
            break;
         }
         buf[pos++] = buf[off++];
      }
      name.len = pos - mark;
      name.off = mark;
   }

   /**
    * This extracts a parameter value from a path segment. The
    * parameter value consists of a sequence of pchars and some
    * escape sequences. The parameter value is buffered so that
    * the name and values can be paired. The end of the value
    * is determined as the end of the buffer or the last pchar.
    */
   private void value(){
      int mark = off;
      int pos = off;

      while(off < count){
         if(buf[off]=='%'){ /* escaped */
            escape();
         }else if(!pchar(buf[off])) {
            break;
         }
         buf[pos++] = buf[off++];
      }
      value.len = pos - mark;
      value.off = mark;
   }

   /**
    * This method adds the name and value to a map so that the next
    * name and value can be collected. The name and value are added
    * to the map as string objects. Once added to the map the
    * Token objects are set to have zero length so they
    * can be reused to collect further values. This will add the
    * values to the map as an array of type string. This is done so
    * that if there are multiple values that they can be stored.
    */
   private void insert(){
      if(value.length() > 0){
         if(name.length() > 0)
            insert(name,value);
      }
      name.clear();
      value.clear();
   }

   /**
    * This will add the given name and value to the parameters map.
    * This will only store a single value per parameter name, so
    * only the parameter that was latest encountered will be saved.
    * The getQuery method can be used to collect
    * the parameter values using the parameter name.
    *
    * @param name this is the name of the value to be inserted
    * @param value this is the value of a that is to be inserted
    */
   private void insert(Token name, Token value){
      insert(name.toString(), value.toString());
   }

   /**
    * This will add the given name and value to the parameters map.
    * This will only store a single value per parameter name, so
    * only the parameter that was latest encountered will be saved.
    * The getQuery method can be used to collect
    * the parameter values using the parameter name.
    *
    * @param name this is the name of the value to be inserted
    * @param value this is the value of a that is to be inserted
    */
   private void insert(String name, String value) {
      param.put(name, value);
   }

   /**
    * This converts an encountered escaped sequence, that is all
    * embedded hexidecimal characters into a native UCS character
    * value. This does not take any characters from the stream it
    * just prepares the buffer with the correct byte. The escaped
    * sequence within the URI will be interpreded as UTF-8.
    *

    * This will leave the next character to read from the buffer
    * as the character encoded from the URI. If there is a fully
    * valid escaped sequence, that is "%" HEX HEX.
    * This decodes the escaped sequence using UTF-8 encoding, all
    * encoded sequences should be in UCS-2 to fit in a Java char.
    */
   private void escape() {
      int peek = peek(off);

      if(!unicode(peek)) {
         binary(peek);
      }
   }

   /**
    * This method determines, using a peek character, whether the
    * sequence of escaped characters within the URI is binary data.
    * If the data within the escaped sequence is binary then this
    * will ensure that the next character read from the URI is the
    * binary octet. This is used strictly for backward compatible
    * parsing of URI strings, binary data should never appear.
    *
    * @param peek this is the first escaped character from the URI
    *
    * @return currently this implementation always returns true
    */
   private boolean binary(int peek) {
      if(off + 2 < count) {
         off += 2;
         buf[off]= bits(peek);
      }
      return true;
   }

   /**
    * This method determines, using a peek character, whether the
    * sequence of escaped characters within the URI is in UTF-8. If
    * a UTF-8 character can be successfully decoded from the URI it
    * will be the next character read from the buffer. This can
    * check for both UCS-2 and UCS-4 characters. However, because
    * the Java char can only hold UCS-2, the UCS-4
    * characters will have only the low order octets stored.
    *

    * The WWW Consortium provides a reference implementation of a
    * UTF-8 decoding for Java, in this the low order octets in the
    * UCS-4 sequence are used for the character. So, in the
    * absence of a defined behaviour, the W3C behaviour is assumed.
    *
    * @param peek this is the first escaped character from the URI
    *
    * @return this returns true if a UTF-8 character is decoded
    */
   private boolean unicode(int peek) {
      if((peek & 0x80) == 0x00){
         return unicode(peek, 0);
      }
      if((peek & 0xe0) == 0xc0){
         return unicode(peek & 0x1f, 1);
      }
      if((peek & 0xf0) == 0xe0){
         return unicode(peek & 0x0f, 2);
      }
      if((peek & 0xf8) == 0xf0){
         return unicode(peek & 0x07, 3);
      }
      if((peek & 0xfc) == 0xf8){
         return unicode(peek & 0x03, 4);
      }
      if((peek & 0xfe) == 0xfc){
         return unicode(peek & 0x01, 5);
      }
      return false;
   }

   /**
    * This method will decode the specified amount of escaped
    * characters from the URI and convert them into a single Java
    * UCS-2 character. If there are not enough characters within
    * the URI then this will return false and leave the URI alone.
    *

    * The number of characters left is determined from the first
    * UTF-8 octet, as specified in RFC 2279, and because this is
    * a URI there must that number of "%" HEX HEX
    * sequences left. If successful the next character read is
    * the UTF-8 sequence decoded into a native UCS-2 character.
    *
    * @param peek contains the bits read from the first UTF octet
    * @param more this specifies the number of UTF octets left
    *
    * @return this returns true if a UTF-8 character is decoded
    */
   private boolean unicode(int peek, int more) {
      if(off + more * 3 >= count) {
         return false;
      }
      return unicode(peek,more,off);
   }

   /**
    * This will decode the specified amount of trailing UTF-8 bits
    * from the URI. The trailing bits are those following the first
    * UTF-8 octet, which specifies the length, in octets, of the
    * sequence. The trailing octets are if the form 10xxxxxx, for
    * each of these octets only the last six bits are valid UCS
    * bits. So a conversion is basically an accumulation of these.
    *

    * If at any point during the accumulation of the UTF-8 bits
    * there is a parsing error, then parsing is aborted an false
    * is returned, as a result the URI is left unchanged.
    *
    * @param peek bytes that have been accumulated from the URI
    * @param more this specifies the number of UTF octets left
    * @param pos this specifies the position the parsing begins
    *
    * @return this returns true if a UTF-8 character is decoded
    */
   private boolean unicode(int peek, int more, int pos) {
      while(more-- > 0) {
         if(buf[pos] == '%'){
            int next = pos + 3;
            int hex = peek(next);

            if((hex & 0xc0) == 0x80){
               peek = (peek<<6)|(hex&0x3f);
               pos = next;
               continue;
            }
         }
         return false;
      }
      if(pos + 2 < count) {
         off = pos + 2;
         buf[off]= bits(peek);
      }
      return true;
   }

   /**
    * Defines behaviour for UCS-2 versus UCS-4 conversion from four
    * octets. The UTF-8 encoding scheme enables UCS-4 characters to
    * be encoded and decodeded. However, Java supports the 16-bit
    * UCS-2 character set, and so the 32-bit UCS-4 character set is
    * not compatable. This basically decides what to do with UCS-4.
    *
    * @param data up to four octets to be converted to UCS-2 format
    *
    * @return this returns a native UCS-2 character from the int
    */
   private char bits(int data) {
      return (char)data;
   }

   /**
    * This will return the escape expression specified from the URI
    * as an integer value of the hexidecimal sequence. This does
    * not make any changes to the buffer it simply checks to see if
    * the characters at the position specified are an escaped set
    * characters of the form "%" HEX HEX, if so, then
    * it will convert that hexidecimal string  in to an integer
    * value, or -1 if the expression is not hexidecimal.
    *
    * @param pos this is the position the expression starts from
    *
    * @return the integer value of the hexidecimal expression
    */
   private int peek(int pos) {
      if(buf[pos] == '%'){
         if(count <= pos + 2) {
            return -1;
         }
         char high = buf[pos + 1];
         char low = buf[pos + 2];

         return convert(high, low);
      }
      return -1;
   }

   /**
    * This will convert the two hexidecimal characters to a real
    * integer value, which is returned. This requires characters
    * within the range of 'A' to 'F' and 'a' to 'f', and also
    * the digits '0' to '9'. The characters encoded using the
    * ISO-8859-1 encoding scheme, if the characters are not with
    * in the range specified then this returns -1.
    *
    * @param high this is the high four bits within the integer
    * @param low this is the low four bits within the integer
    *
    * @return this returns the indeger value of the conversion
    */
   private int convert(char high, char low) {
      int hex = 0x00;

      if(hex(high) && hex(low)){
         if('A' <= high && high <= 'F'){
            high -= 'A' - 'a';
         }
         if(high >= 'a') {
            hex ^= (high-'a')+10;
         } else {
            hex ^= high -'0';
         }
         hex <<= 4;

         if('A' <= low && low <= 'F') {
            low -= 'A' - 'a';
         }
         if(low >= 'a') {
            hex ^= (low-'a')+10;
         } else {
            hex ^= low-'0';
         }
         return hex;
      }
      return -1;
   }

   /**
    * This is used to determine wheather a char is a hexidecimal
    * char or not. A hexidecimal character is consdered
    * to be a character within the range of 0 - 9 and
    * between a - f and A - F. This will
    * return true if the character is in this range.
    *
    * @param ch this is the character which is to be determined here
    *
    * @return true if the character given has a hexidecimal value
    */
   private boolean hex(char ch) {
      if(ch >= '0' && ch <= '9') {
         return true;
      } else if(ch >='a' && ch <= 'f') {
         return true;
      } else if(ch >= 'A' && ch <= 'F') {
         return true;
      }
      return false;
   }

   /**
    * This is a character set defined by RFC 2396 it is used to
    * determine the valididity of certain chars
    * within a Uniform Resource Identifier. RFC 2396 defines
    * an unreserved char as alphanum | mark.
    *
    * @param c the character value that is being checked
    *
    * @return true if the character has an unreserved value
    */
   private boolean unreserved(char c){
      return mark(c) || alphanum(c);
   }

   /**
    * This is used to determine wheather or not a given unicode
    * character is an alphabetic character or a digit character.
    * That is withing the range 0 - 9 and between
    * a - z it uses iso-8859-1 to
    * compare the character.
    *
    * @param c the character value that is being checked
    *
    * @return true if the character has an alphanumeric value
    */
   private boolean alphanum(char c){
      return digit(c) || alpha(c);
   }

   /**
    * This is used to determine wheather or not a given unicode
    * character is an alphabetic character. This uses encoding
    * iso-8859-1 to compare the characters.
    *
    * @param c the character value that is being checked
    *
    * @return true if the character has an alphabetic value
    */
   private boolean alpha(char c){
      return (c <= 'z' && 'a' <= c) ||
       (c <= 'Z' && 'A' <= c);
   }

   /**
    * This is a character set defined by RFC 2396 it checks
    * the valididity of cetain chars within a uniform resource
    * identifier. The RFC 2396 defines a mark char as "-",
    * "_", ".", "!", "~", "*", "'", "(", ")".
    *
    * @param c the character value that is being checked
    *
    * @return true if the character is a mark character
    */
   private boolean mark(char c){
      switch(c){
      case '-': case '_': case '.':
      case '!': case '~': case '*':
      case '\'': case '(': case ')':
         return true;
      default:
         return false;
      }
   }

   /**
    * This is a character set defined by RFC 2396 it is used to check
    * the valididity of cetain chars within a generic uniform resource
    * identifier. The RFC 2396 defines a pchar char as unreserved or
    * escaped or one of the following characters ":", "@", "=",
    * "&", "+", "$", "," this will not check to see if the
    * char is an escaped char, that is % HEX HEX. Because
    * this takes 3 chars.
    *
    * @param c the character value that is being checked
    *
    * @return true if the character is a pchar character
    */
   private boolean pchar(char c){
      switch(c){
      case '@': case '&': case '=':
      case '+': case '$': case ',':
      case ':':
         return true;
      default:
         return unreserved(c);
      }
   }

   /**
    * This is a character set defined by RFC 2396, it checks the
    * valididity of certain chars in a uniform resource identifier.
    * The RFC 2396 defines a reserved char as ";", "/", "?",
    * ":", "@", "&", "=", "+", "$", ",".
    *
    * @param c the character value that is being checked
    *
    * @return true if the character is a reserved character
    */
   private boolean reserved(char c){
      switch(c){
      case ';': case '/': case '?':
      case '@': case '&': case ':':
      case '=': case '+': case '$':
      case ',':
         return true;
      default:
         return false;
      }
   }

   /**
    * This is used to convert this URI object into a String
    * object. This will only convert the parts of the URI that exist, so
    * the URI may not contain the domain or the query part and it will
    * not contain the path parameters. If the URI contains all these
    * parts then it will return somthing like
    *

    * scheme://host:port/path/path?querypart
    *

    *

    * It can return /path/path?querypart style relative
    * URI's. If any of the parts are set to null then that part will be
    * missing, for example if setDomain method is invoked
    * with a null parameter then the domain and port will be missing
    * from the resulting URI. If the path part is set to null using the
    * setPath then the path will be /. An
    * example URI with the path part of null would be
    *

    * scheme://host:port/?querypart
    *

    *
    * @return the URI with only the path part and the non-null optional
    * parts of the uniform resource identifier
    */
   public String toString() {
      return (scheme.length() > 0 ? scheme +"://": "") +
      (domain.length() > 0 ? domain +
       (port > 0 ? ":"+port : "") : "")+ getPath() +
         (param.size() > 0 ? param  : "")+
         (query.length()>0?"?"+query :"");
   }

   /**
    * The ParameterMap is uses to store the parameters
    * that are to be encoded in to the address. This will append all
    * of the parameters to the end of the path. These can later be
    * extracted by parsing the address.
    *
    * @author Niall Gallagher
    */
   private class ParameterMap extends KeyMap<String> {

      /**
       * This will return the parameters encoded in such a way that
       * it can be appended to the end of the path. These parameters
       * can be added to the address such that they do not form a
       * query parameter. Values such as session identifiers are
       * often added as the path parameters to the address.
       *
       * @return this returns the representation of the parameters
       */
      private String encode() {
         StringBuilder text = new StringBuilder();

         for(String name : param) {
            String value = param.get(name);

            text.append(";");
            text.append(name);

            if(value != null) {
               text.append("=");
               text.append(value);;
            }
         }
         return text.toString();
      }

      /**
       * This will return the parameters encoded in such a way that
       * it can be appended to the end of the path. These parameters
       * can be added to the address such that they do not form a
       * query parameter. Values such as session identifiers are
       * often added as the path parameters to the address.
       *
       * @return this returns the representation of the parameters
       */
      public String toString() {
         return encode();
      }
   }

   /**
    * This is used as an alternative to the ParseBuffer
    * for extracting tokens from the URI without allocating memory.
    * This will basically mark out regions within the buffer which are
    * used to represent the token. When the token value is required
    * the region is used to create a String object.
    */
   private class Token {

      /**
       * This can be used to override the value for this token.
       */
      public String value;

      /**
       * This represents the start offset within the buffer.
       */
      public int off;

      /**
       * This represents the number of charters in the token.
       */
      public int len;

      /**
       * If the Token is to be reused this will clear
       * all previous data. Clearing the buffer allows it to be
       * reused if there is a new URI to be parsed. This ensures
       * that a null is returned if the token length is zero.
       */
      public void clear() {
         value = null;
         len = 0;
      }

      /**
       * This is used to determine the number of characters this
       * token contains. This is used rather than accessing the
       * length directly so that the value the token represents
       * can be overridden easily without upsetting the token.
       *
       * @return this returns the number of characters this uses
       */
      public int length() {
         if(value == null){
            return len;
         }
         return value.length();
      }

      /**
       * This method will convert the Token into it's
       * String equivelant. This will firstly check
       * to see if there is a value, for the string representation,
       * if there is the value is returned, otherwise the region
       * is converted into a String and returned.
       *
       * @return this returns a value representing the token
       */
      public String toString() {
         if(value != null) {
            return value;
         }
         if(len > 0) {
            value = new String(buf,off,len);
         }
         return value;
      }
   }
}


text:
<ion-header>
    <ion-navbar core-back-button>
        <ion-title>{{ 'core.settings.about' | translate }}</ion-title>
    </ion-navbar>
</ion-header>
<ion-content>
    <ion-item text-wrap>

{{ appName }} {{ versionName }}

    </ion-item>
    <ion-item-group>
        <ion-item-divider text-wrap color="light">
            {{ 'core.settings.license' | translate }}
        </ion-item-divider>
        <ion-item text-wrap>

Apache 2.0

http://www.apache.org/licenses/LICENSE-2.0

        </ion-item>
    </ion-item-group>
    <ion-item-group *ngIf="privacyPolicy">
        <ion-item-divider text-wrap color="light">
            {{ 'core.settings.privacypolicy' | translate }}
        </ion-item-divider>
        <ion-item text-wrap>

{{ privacyPolicy }}

        </ion-item>
    </ion-item-group>
    <ion-item-group>
        <ion-item-divider text-wrap color="light">
            {{ 'core.settings.deviceinfo' | translate }}
        </ion-item-divider>
        <ion-item text-wrap *ngIf="versionName">

{{ 'core.settings.versionname' | translate}}

{{ versionName }}

        </ion-item>
        <ion-item text-wrap *ngIf="versionCode">

{{ 'core.settings.versioncode' | translate}}

{{ versionCode }}

        </ion-item>
        <ion-item text-wrap *ngIf="compilationTime">

{{ 'core.settings.compilationtime' | translate }}

{{ compilationTime | coreFormatDate: "LLL Z" }}

        </ion-item>
        <ion-item text-wrap *ngIf="fileSystemRoot">

{{ 'core.settings.filesystemroot' | translate}}

{{ fileSystemRoot }}

{{ fileSystemRoot }}

        </ion-item>
        <ion-item text-wrap *ngIf="navigator && navigator.userAgent">

{{ 'core.settings.navigatoruseragent' | translate}}

{{ navigator.userAgent }}

        </ion-item>
        <ion-item text-wrap *ngIf="navigator && navigator.language">

{{ 'core.settings.navigatorlanguage' | translate}}

{{ navigator.language }}

        </ion-item>
        <ion-item text-wrap *ngIf="locationHref">

{{ 'core.settings.locationhref' | translate}}

{{ locationHref }}

        </ion-item>
        <ion-item text-wrap *ngIf="appReady">

{{ 'core.settings.appready' | translate}}

{{ appReady | translate }}

        </ion-item>
        <ion-item text-wrap *ngIf="deviceType">

{{ 'core.settings.displayformat' | translate}}

{{ deviceType | translate }}

        </ion-item>
        <ion-item text-wrap *ngIf="deviceOs">

{{ 'core.settings.deviceos' | translate}}

{{ deviceOs | translate }}

        </ion-item>
        <ion-item text-wrap *ngIf="currentLanguage">

{{ 'core.settings.currentlanguage' | translate}}

{{ currentLanguage }}

        </ion-item>
        <ion-item text-wrap *ngIf="networkStatus">

{{ 'core.settings.networkstatus' | translate}}

{{ networkStatus | translate }}

        </ion-item>
        <ion-item text-wrap *ngIf="wifiConnection">

{{ 'core.settings.wificonnection' | translate}}

{{ wifiConnection | translate }}

        </ion-item>
        <ion-item text-wrap *ngIf="deviceWebWorkers">

{{ 'core.settings.devicewebworkers' | translate}}

{{ deviceWebWorkers | translate }}

        </ion-item>
        <ion-item text-wrap *ngIf="device && device.cordova">

{{ 'core.settings.cordovaversion' | translate}}

{{ device.cordova }}

        </ion-item>
        <ion-item text-wrap *ngIf="device && device.platform">

{{ 'core.settings.cordovadeviceplatform' | translate}}

{{ device.platform }}

        </ion-item>
        <ion-item text-wrap *ngIf="device && device.version">

{{ 'core.settings.cordovadeviceosversion' | translate}}

{{ device.version }}

        </ion-item>
        <ion-item text-wrap *ngIf="device && device.model">

{{ 'core.settings.cordovadevicemodel' | translate}}

{{ device.model }}

        </ion-item>
        <ion-item text-wrap *ngIf="device && device.uuid">

{{ 'core.settings.cordovadeviceuuid' | translate}}

{{ device.uuid }}

        </ion-item>
        <ion-item text-wrap *ngIf="localNotifAvailable">

{{ 'core.settings.localnotifavailable' | translate}}

{{ localNotifAvailable | translate }}

        </ion-item>
    </ion-item-group>
</ion-content>

redpajama_set_name: RedPajamaGithub


text:
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Joints;
using FarseerPhysics.Factories;
using FarseerPhysics.Samples.DrawingSystem;
using FarseerPhysics.Samples.ScreenSystem;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace FarseerPhysics.Samples.Demos.Prefabs
{
    public class Spider
    {
        private const float SpiderBodyRadius = 0.65f;
        private bool _kneeFlexed;
        private float _kneeTargetAngle = -0.4f;
        private AngleJoint _leftKneeAngleJoint;
        private AngleJoint _leftShoulderAngleJoint;
        private Sprite _lowerLeg;
        private Vector2 _lowerLegSize = new Vector2(1.8f, 0.3f);

        private AngleJoint _rightKneeAngleJoint;
        private AngleJoint _rightShoulderAngleJoint;
        private float _s;
        private SpriteBatch _batch;
        private bool _shoulderFlexed;
        private float _shoulderTargetAngle = -0.2f;
        private Sprite _torso;
        private Sprite _upperLeg;
        private Vector2 _upperLegSize = new Vector2(1.8f, 0.3f);

        private Body _circle;
        private Body _leftLower;
        private Body _leftUpper;
        private Body _rightLower;
        private Body _rightUpper;

        public Spider(World world, ScreenManager screenManager, Vector2 position)
        {
            _batch = screenManager.SpriteBatch;

            //Load bodies
            _circle = BodyFactory.CreateCircle(world, SpiderBodyRadius, 0.1f, position);
            _circle.BodyType = BodyType.Dynamic;

            //Left upper leg
            _leftUpper = BodyFactory.CreateRectangle(world, _upperLegSize.X, _upperLegSize.Y, 0.1f, _circle.Position - new Vector2(SpiderBodyRadius, 0f) - new Vector2(_upperLegSize.X / 2f, 0f));
            _leftUpper.BodyType = BodyType.Dynamic;

            //Left lower leg
            _leftLower = BodyFactory.CreateRectangle(world, _lowerLegSize.X, _lowerLegSize.Y, 0.1f, _circle.Position - new Vector2(SpiderBodyRadius, 0f) - new Vector2(_upperLegSize.X, 0f) - new Vector2(_lowerLegSize.X / 2f, 0f));
            _leftLower.BodyType = BodyType.Dynamic;

            //Right upper leg
            _rightUpper = BodyFactory.CreateRectangle(world, _upperLegSize.X, _upperLegSize.Y, 0.1f, _circle.Position + new Vector2(SpiderBodyRadius, 0f) + new Vector2(_upperLegSize.X / 2f, 0f));
            _rightUpper.BodyType = BodyType.Dynamic;

            //Right lower leg
            _rightLower = BodyFactory.CreateRectangle(world, _lowerLegSize.X, _lowerLegSize.Y, 0.1f, _circle.Position + new Vector2(SpiderBodyRadius, 0f) + new Vector2(_upperLegSize.X, 0f) + new Vector2(_lowerLegSize.X / 2f, 0f));
            _rightLower.BodyType = BodyType.Dynamic;

            //Create joints
            JointFactory.CreateRevoluteJoint(world, _circle, _leftUpper, new Vector2(_upperLegSize.X / 2f, 0f));
            _leftShoulderAngleJoint = JointFactory.CreateAngleJoint(world, _circle, _leftUpper);
            _leftShoulderAngleJoint.MaxImpulse = 3f;

            JointFactory.CreateRevoluteJoint(world, _circle, _rightUpper, new Vector2(-_upperLegSize.X / 2f, 0f));
            _rightShoulderAngleJoint = JointFactory.CreateAngleJoint(world, _circle, _rightUpper);
            _rightShoulderAngleJoint.MaxImpulse = 3f;

            JointFactory.CreateRevoluteJoint(world, _leftUpper, _leftLower, new Vector2(_lowerLegSize.X / 2f, 0f));
            _leftKneeAngleJoint = JointFactory.CreateAngleJoint(world, _leftUpper, _leftLower);
            _leftKneeAngleJoint.MaxImpulse = 3f;

            JointFactory.CreateRevoluteJoint(world, _rightUpper, _rightLower, new Vector2(-_lowerLegSize.X / 2f, 0f));
            _rightKneeAngleJoint = JointFactory.CreateAngleJoint(world, _rightUpper, _rightLower);
            _rightKneeAngleJoint.MaxImpulse = 3;

            //GFX
            AssetCreator creator = screenManager.Assets;
            _torso = new Sprite(creator.CircleTexture(SpiderBodyRadius, MaterialType.Waves, Color.Gray, 1f));
            _upperLeg = new Sprite(creator.TextureFromShape(_leftUpper.FixtureList[0].Shape, MaterialType.Blank, Color.DimGray, 1f));
            _lowerLeg = new Sprite(creator.TextureFromShape(_leftLower.FixtureList[0].Shape, MaterialType.Blank, Color.DarkSlateGray, 1f));
        }

        public void Update(GameTime gameTime)
        {
            _s += gameTime.ElapsedGameTime.Milliseconds;
            if (_s > 4000)
            {
                _s = 0;

                _kneeFlexed = !_kneeFlexed;
                _shoulderFlexed = !_shoulderFlexed;

                if (_kneeFlexed)
                    _kneeTargetAngle = -1.4f;
                else
                    _kneeTargetAngle = -0.4f;

                if (_kneeFlexed)
                    _shoulderTargetAngle = -1.2f;
                else
                    _shoulderTargetAngle = -0.2f;
            }

            _leftKneeAngleJoint.TargetAngle = _kneeTargetAngle;
            _rightKneeAngleJoint.TargetAngle = -_kneeTargetAngle;

            _leftShoulderAngleJoint.TargetAngle = _shoulderTargetAngle;
            _rightShoulderAngleJoint.TargetAngle = -_shoulderTargetAngle;
        }

        public void Draw()
        {
            _batch.Draw(_lowerLeg.Texture, ConvertUnits.ToDisplayUnits(_leftLower.Position), null, Color.White, _leftLower.Rotation, _lowerLeg.Origin, 1f, SpriteEffects.None, 0f);
            _batch.Draw(_lowerLeg.Texture, ConvertUnits.ToDisplayUnits(_rightLower.Position), null, Color.White, _rightLower.Rotation, _lowerLeg.Origin, 1f, SpriteEffects.None, 0f);

            _batch.Draw(_upperLeg.Texture, ConvertUnits.ToDisplayUnits(_leftUpper.Position), null, Color.White, _leftUpper.Rotation, _upperLeg.Origin, 1f, SpriteEffects.None, 0f);
            _batch.Draw(_upperLeg.Texture, ConvertUnits.ToDisplayUnits(_rightUpper.Position), null, Color.White, _rightUpper.Rotation, _upperLeg.Origin, 1f, SpriteEffects.None, 0f);

            _batch.Draw(_torso.Texture, ConvertUnits.ToDisplayUnits(_circle.Position), null, Color.White, _circle.Rotation, _torso.Origin, 1f, SpriteEffects.None, 0f);
        }
    }
}


text:
<?xml version="1.0" encoding="utf-8"?>
<selector
  xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true" android:drawable="@drawable/bg_img_btn_dish_item_dec_press" />
    <item android:state_pressed="true" android:drawable="@drawable/bg_img_btn_dish_item_dec_press" />
    <item android:state_enabled="false" android:drawable="@drawable/bg_img_btn_dish_item_dec_disabled" />
    <item android:drawable="@drawable/bg_img_btn_dish_item_dec_press" />
</selector>


text:
//
//  IMModel.m
//  05-RuntimeDic2Model
//
//  Created by feng on 2017/1/3.
//  Copyright © 2017年 feng. All rights reserved.
//

#import "IMModel.h"

@implementation IMModel

/*
+ (IMModel *)modelWithDict:(NSDictionary *)dict{

    IMModel *model = [[IMModel alloc] init];

    [model setValuesForKeysWithDictionary:dict];

    return model;

}

- (void)setValue:(id)value forUndefinedKey:(NSString *)key{

    if ([key isEqualToString:@"id"]) {
        _ID = value;

    }

}

 */

@end


text:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <CheckBox
        android:id="@+id/cbBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical" >
    </CheckBox>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:id="@+id/linearLayout1"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_marginLeft="5dp"
            android:orientation="vertical"
            android:layout_weight="1" >

            <TextView
                android:id="@+id/tvDescr"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:text=""
                android:textSize="20sp"
                android:gravity="center_vertical"
                android:textColor="#ffffff">
            </TextView>

            <TextView
                android:id="@+id/tvPrice"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:text=""
                android:visibility="gone">
            </TextView>
        </LinearLayout>

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_gravity="right"
            android:visibility="gone">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="10m"
                android:id="@+id/plyrDis"
                android:layout_gravity="right" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>


text:
static const CGFloat kTOCropOverLayerCornerWidth = 20.0f;

@interface TOCropOverlayView ()

@property (nonatomic, strong) NSArray *horizontalGridLines;
@property (nonatomic, strong) NSArray *verticalGridLines;

@property (nonatomic, strong) NSArray *outerLineViews;   //top, right, bottom, left

@property (nonatomic, strong) NSArray *topLeftLineViews; //vertical, horizontal
@property (nonatomic, strong) NSArray *bottomLeftLineViews;
@property (nonatomic, strong) NSArray *bottomRightLineViews;
@property (nonatomic, strong) NSArray *topRightLineViews;

- (void)setup;
- (void)layoutLines;

@end

@implementation TOCropOverlayView

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        self.clipsToBounds = NO;
        [self setup];
    }

    return self;
}

- (void)setup
{
    UIView *(^newLineView)(void) = ^UIView *(void){
        UIView *newLine = [[UIView alloc] initWithFrame:CGRectZero];
        newLine.backgroundColor = [UIColor whiteColor];
//        [self addSubview:newLine];
        return newLine;
    };

    _outerLineViews     = @[newLineView(), newLineView(), newLineView(), newLineView()];

    _topLeftLineViews   = @[newLineView(), newLineView()];
    _bottomLeftLineViews = @[newLineView(), newLineView()];
    _topRightLineViews  = @[newLineView(), newLineView()];
    _bottomRightLineViews = @[newLineView(), newLineView()];

    _horizontalGridLines = @[newLineView(), newLineView()];
    _verticalGridLines = @[newLineView(), newLineView()];

    self.hidden = YES;
}

- (void)setFrame:(CGRect)frame
{
    [super setFrame:frame];

    if (_outerLineViews)
        [self layoutLines];
}

- (void)didMoveToSuperview
{
    [super didMoveToSuperview];

    if (_outerLineViews)
        [self layoutLines];
}

- (void)layoutLines
{
    CGSize boundsSize = self.bounds.size;

    //border lines
    for (NSInteger i = 0; i < 4; i++) {
        UIView *lineView = self.outerLineViews[i];

        CGRect frame = CGRectZero;
        switch (i) {
            case 0: frame = (CGRect){0,-1.0f,boundsSize.width+2.0f, 1.0f}; break; //top
            case 1: frame = (CGRect){boundsSize.width,0.0f,1.0f,boundsSize.height}; break; //right
            case 2: frame = (CGRect){-1.0f,boundsSize.height,boundsSize.width+2.0f,1.0f}; break; //bottom
            case 3: frame = (CGRect){-1.0f,0,1.0f,boundsSize.height+1.0f}; break; //left
        }

        lineView.frame = frame;
    }

    //corner liness
    NSArray *cornerLines = @[self.topLeftLineViews, self.topRightLineViews, self.bottomRightLineViews, self.bottomLeftLineViews];
    for (NSInteger i = 0; i < 4; i++) {
        NSArray *cornerLine = cornerLines[i];

        CGRect verticalFrame, horizontalFrame;
        switch (i) {
            case 0: //top left
                verticalFrame = (CGRect){-3.0f,-3.0f,3.0f,kTOCropOverLayerCornerWidth+3.0f};
                horizontalFrame = (CGRect){0,-3.0f,kTOCropOverLayerCornerWidth,3.0f};
                break;
            case 1: //top right
                verticalFrame = (CGRect){boundsSize.width,-3.0f,3.0f,kTOCropOverLayerCornerWidth+3.0f};
                horizontalFrame = (CGRect){boundsSize.width-kTOCropOverLayerCornerWidth,-3.0f,kTOCropOverLayerCornerWidth,3.0f};
                break;
            case 2: //bottom right
                verticalFrame = (CGRect){boundsSize.width,boundsSize.height-kTOCropOverLayerCornerWidth,3.0f,kTOCropOverLayerCornerWidth+3.0f};
                horizontalFrame = (CGRect){boundsSize.width-kTOCropOverLayerCornerWidth,boundsSize.height,kTOCropOverLayerCornerWidth,3.0f};
                break;
            case 3: //bottom left
                verticalFrame = (CGRect){-3.0f,boundsSize.height-kTOCropOverLayerCornerWidth,3.0f,kTOCropOverLayerCornerWidth};
                horizontalFrame = (CGRect){-3.0f,boundsSize.height,kTOCropOverLayerCornerWidth+3.0f,3.0f};
                break;
        }

        [cornerLine[0] setFrame:verticalFrame];
        [cornerLine[1] setFrame:horizontalFrame];
    }

    //grid lines - horizontal
    CGFloat thickness = 1.0f / [[UIScreen mainScreen] scale];
    NSInteger numberOfLines = self.horizontalGridLines.count;
    CGFloat padding = (CGRectGetHeight(self.bounds) - (thickness*numberOfLines)) / (numberOfLines + 1);
    for (NSInteger i = 0; i < numberOfLines; i++) {
        UIView *lineView = self.horizontalGridLines[i];
        CGRect frame = CGRectZero;
        frame.size.height = thickness;
        frame.size.width = CGRectGetWidth(self.bounds);
        frame.origin.y = (padding * (i+1)) + (thickness * i);
        lineView.frame = frame;
    }

    //grid lines - vertical
    numberOfLines = self.verticalGridLines.count;
    padding = (CGRectGetWidth(self.bounds) - (thickness*numberOfLines)) / (numberOfLines + 1);
    for (NSInteger i = 0; i < numberOfLines; i++) {
        UIView *lineView = self.verticalGridLines[i];
        CGRect frame = CGRectZero;
        frame.size.width = thickness;
        frame.size.height = CGRectGetHeight(self.bounds);
        frame.origin.x = (padding * (i+1)) + (thickness * i);
        lineView.frame = frame;
    }
}

- (void)setGridHidden:(BOOL)hidden animated:(BOOL)animated
{
    _gridHidden = hidden;

    if (animated == NO) {
        for (UIView *lineView in self.horizontalGridLines) {
            lineView.alpha = hidden ? 0.0f : 1.0f;
        }

        for (UIView *lineView in self.verticalGridLines) {
            lineView.alpha = hidden ? 0.0f : 1.0f;
        }

        return;
    }

    [UIView animateWithDuration:hidden?0.35f:0.2f animations:^{
        for (UIView *lineView in self.horizontalGridLines)
            lineView.alpha = hidden ? 0.0f : 1.0f;

        for (UIView *lineView in self.verticalGridLines)
            lineView.alpha = hidden ? 0.0f : 1.0f;
    }];
}

- (void)setGridHidden:(BOOL)gridHidden
{
    [self setGridHidden:gridHidden animated:NO];
}

@end


text:
import * as PIXI from 'pixi.js';
import utils  from '../../../utils';
import AnimationManager  from './../utils/AnimationManager';
import Entity  from './../Entity';
export default class Nest extends Entity {

    constructor(game, team) {

    	super(game);

        this.game = game;
        this.type = 'spawner';
        this.name = 'tower';

        this.team = team;

        this.scaleFator = 2;

        this.base = new PIXI.Container();
        this.roundBase = new PIXI.Graphics();
        this.roundBase.beginFill(0xFFFFFF);
        this.roundBase.drawCircle(0,0,100);
        this.roundBase.scale.y = 0.4;
        this.roundBase.alpha = 0.1;
        this.base.addChild(this.roundBase);
        this.addChild(this.base);

        this.animationContainer = new PIXI.Container();
        this.animationContainer.x = 0
        this.animationContainer.y = 0
        this.addChild(this.animationContainer);

        this.radius = 150;
        this.externalRadius = 300;
        // this.debugCollision();

        this.static = true;

        this.waitingNext = 5 * Math.random() + 1;
        this.sinScale = Math.random();
        this.life = 10000;

        this.enemiesList = [];
        // this.build();

        this.hitting = false;
        this.hitTimer = -1;

        this.slots = [];

        this.attacking = false;
        this.attackTimer = 1;
        this.waypointList = [];

        this.spawnTime = 30;

        this.spawnQuant = 4//this.team == 1?8:5;
        this.spawDistance = 1;

        this.currentWave = 0;

        this.actionTimer = -1;
        this.action = null;

        this.totalEntities = 0;
        this.totalWaves = 0;
        this.totalKilled = 0;

        this.currentEntities = [];

        this.actionTimer = 3;
        this.action = this.testCollisions;

    }

    hit(power){
        return
        super.hit(power);
        this.hitTimer = 0.5;
        this.hitting = true;
    }
    addWaypoint (x,y) {
        this.waypointList.push({x:x, y:y});
    }
    startSpawn () {
        this.currentWave = 0;
        this.totalWaves ++;
        this.addEntity();
    }
    addEntity () {

        this.currentWave ++;

        // if(this.currentWave > this.spawnQuant){
        //     this.actionTimer = this.spawnTime;
        //     //this.action = this.startSpawn;
        //     return
        // }
        this.totalEntities ++;
        // TweenLite.from(this.animationContainer.scale, 0.8, {x:0.9, y:0.9, ease:'easeOutElastic'})
        let type = 'nest'//this.currentWave > this.spawnQuant -2?'tanker':'standard';

        let angle = this.slots[this.currentSlot].angle /180*3.14;
        let randomRadius = this.getRadius() + 20 * Math.random();
        let targetPosition = {x:this.x + Math.sin(angle) * randomRadius, y:this.y + Math.cos(angle) * randomRadius}

        let ent = this.game.addEnemy(type, {x:targetPosition.x, y:targetPosition.y}, this.waypointList, this.team);

        ent.setNestCenter({x:this.x, y:this.y}, this.getExternalRadius());
        this.currentSlot ++;
        //this.game.addEnemy('tomato', {x:this.x, y:this.y + Math.random() * this.getRadius() - this.getRadius()/2}, this.waypointList, this.team);
        // this.actionTimer = this.spawDistance;
        // this.action = this.addEntity;

        this.currentEntities.push(ent);

        ent.start();
    }

    updateBaseColor ( ) {
        if(this.hitting){
            this.roundBase.tint = 0xFF0000;
        }else{
            this.roundBase.tint = 0;
        }
    }

    start ( ) {
        //this.action = this.startSpawn;
        this.actionTimer = 3;
        //this.addEntity();
    }
    build ( ) {

        let idRock =1// Math.floor(Math.random()*2) + 1;

        this.animationModel = [];
         this.animationModel.push({
            label:'static',
            src:'spawner1/idle/spawner1',
            totalFrames:1,
            startFrame:0,
            animationSpeed:0.4,
            movieClip:null,
            position:{x:10,y:0},
            anchor:{x:0.5,y:1},
            loop:false,
            haveCallback:false,
            singleFrame:true
        });
        // this.animationModel.push({
        //     label:'idle',
        //     src:'pine'+idRock+'00',
        //     totalFrames:22,
        //     startFrame:0,
        //     animationSpeed:0.5,
        //     movieClip:null,
        //     position:{x:10,y:0},
        //     anchor:{x:0.5,y:1},
        //     loop:false,
        //     haveCallback:true,
        // });

        this.animationManager = new AnimationManager(this.animationModel, this.animationContainer);

        this.animationManager.hideAll();
        this.animationManager.stopAll();
        // this.animationManager.changeState('static');

        this.collidable = true;

        let maxAngle = 360
        let totFixed = 10
        for (var i = 1; i < 80; i++) {
            if(i <= totFixed){
                let angle = maxAngle / totFixed * i;
                this.slots.push({angle:angle, entity:null, radius: Math.random() * this.getRadius()/4 + this.getRadius()/4});
                utils.shuffle(this.slots);
            }else{

                this.slots.push({angle:Math.random() * maxAngle, entity:null, radius: Math.random() * this.getRadius()/3 + this.getRadius()/3});
            }
        }

        this.currentSlot = 0;

        for (var i = 0; i < this.spawnQuant; i++) {
           this.addEntity();
        }

    }

    testCollisions (  ) {

        this.actionTimer = 0.3;
        let entityCollisions = this.game.getExternalColisionList(this,['player']);
        // console.log(entityCollisions);
        if(entityCollisions && entityCollisions.length){
            this.ableCollision();
        }else{
            this.removeCollisions();
        }
    }
    removeCollisions (  ) {
        for (var i = 0; i < this.currentEntities.length; i++) {
            this.currentEntities[i].testCollisions = false;
        }
    }
    ableCollision (  ) {
        for (var i = 0; i < this.currentEntities.length; i++) {
            this.currentEntities[i].testCollisions = true;
        }
    }
    update ( delta ) {
        super.update(delta);

        if(this.actionTimer > 0){
            this.actionTimer -= delta;
            if(this.actionTimer <= 0){
                this.action();
            }
        }

        if(this.hitTimer > 0){
            this.hitTimer -= delta;
            if(this.hitTimer <=0 ){
                this.hitting = false;
            }
        }

        for (var i = this.currentEntities.length - 1; i >= 0; i--) {
            if(this.currentEntities[i].disapearing){
                this.totalKilled ++;
                this.currentEntities.splice(i,1);
            }
        }
        this.updateBaseColor();
    }
}


text:
package com.optimizely.ab.android.event_handler;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.content.Context;

import androidx.work.Data;
import androidx.work.WorkerParameters;

import com.optimizely.ab.event.LogEvent;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.io.IOException;

/**
 * Tests {@link EventWorker}
 */
@RunWith(PowerMockRunner.class)
@PrepareForTest({ EventWorker.class, EventHandlerUtils.class, WorkerParameters.class })
@PowerMockIgnore("jdk.internal.reflect.*")
public class EventWorkerUnitTest {

    private WorkerParameters mockWorkParams = PowerMockito.mock(WorkerParameters.class);
    private EventWorker eventWorker = new EventWorker(mock(Context.class), mockWorkParams);

    private String host = "http://www.foo.com";
    private String smallBody = "{\"account_id\":\"1234\"}";

    @Test
    public void dataForEvent() {
        Data data1 = EventWorker.dataForEvent(host, "body-string");
        assertEquals(data1.getString("url"), host);
        assertEquals(data1.getString("body"), "body-string");
        assertNull(data1.getByteArray("bodyCompressed"));
    }

    @Test
    public void dataForCompressedEvent() {
        String base64 = "abc123";
        Data data = EventWorker.dataForCompressedEvent(host, base64);
        assertEquals(data.getString("url"), host);
        assertEquals(data.getString("bodyCompressed"), base64);
        assertNull(data.getString("body"));
    }

    @Test
    public void compressEvent() throws IOException {
        String base64 = "abc123";
        PowerMockito.mockStatic(EventHandlerUtils.class);
        when(EventHandlerUtils.compress(anyString())).thenReturn(base64);

        Data data = EventWorker.compressEvent(host, smallBody);
        assertEquals(data.getString("url"), host);
        assertEquals(data.getString("bodyCompressed"), base64);
        assertNull(data.getString("body"));
    }

    @Test
    public void compressEventWithCompressionFailure() throws IOException {
        PowerMockito.mockStatic(EventHandlerUtils.class);
        PowerMockito.doThrow(new IOException()).when(EventHandlerUtils.class);
        EventHandlerUtils.compress(anyString());  // PowerMockito throws exception on this static method

        // return original body if compress fails

        Data data = EventWorker.compressEvent(host, smallBody);
        assertEquals(data.getString("url"), host);
        assertEquals(data.getString("body"), smallBody);
        assertNull(data.getByteArray("bodyCompressed"));
    }

    @Test
    public void getDataWithSmallEvent() throws IOException {
        LogEvent mockEvent = Mockito.mock(LogEvent.class);
        when(mockEvent.getEndpointUrl()).thenReturn(host);

        int[] sizes = {100, 8000, 9000};
        for (int size : sizes){
            String str = makeLongString(size);

            when(mockEvent.getBody()).thenReturn(str);

            Data data = EventWorker.getData(mockEvent);
            assertEquals(data.getString("url"), host);
            assertEquals(data.getString("body"), str);
            assertNull(data.getByteArray("bodyCompressed"));
        }
    }

    @Test
    public void getDataWithLargeEvent() throws IOException {
        LogEvent mockEvent = Mockito.mock(LogEvent.class);
        when(mockEvent.getEndpointUrl()).thenReturn(host);

        int[] sizes = {10000, 100000};
        for (int size : sizes){
            String str = makeLongString(size);
            String compressed = EventHandlerUtils.compress(str);
            System.out.println("compressed: " + size + " -> " + compressed.length());

            when(mockEvent.getBody()).thenReturn(str);

            Data data = EventWorker.getData(mockEvent);
            assertEquals(data.getString("url"), host);
            assertEquals(data.getString("bodyCompressed"), compressed);
            assertNull(data.getString("body"));
        }
    }

    @Test
    public void getEventBodyFromInputData() throws Exception {
        Data data = EventWorker.dataForEvent(host, smallBody);
        String str = eventWorker.getEventBodyFromInputData(data);
        assertEquals(str, smallBody);
    }

    @Test
    public void getEventBodyFromInputDataCompressed() {
        Data data = EventWorker.compressEvent(host, smallBody);
        String str = eventWorker.getEventBodyFromInputData(data);
        assertEquals(str, smallBody);
    }

    @Test
    public void getEventBodyFromInputDataDecompressFailure() throws Exception {
        Data data = EventWorker.compressEvent(host, smallBody);

        PowerMockito.mockStatic(EventHandlerUtils.class);
        PowerMockito.doThrow(new IOException()).when(EventHandlerUtils.class);
        EventHandlerUtils.decompress(any());  // PowerMockito throws exception on this static method

        String str = eventWorker.getEventBodyFromInputData(data);
        assertNull(str);
    }

    @Test
    public void isEventValid() {
        assertFalse(eventWorker.isEventValid(null, "string"));
        assertFalse(eventWorker.isEventValid("", "string"));
        assertFalse(eventWorker.isEventValid("string", null));
        assertFalse(eventWorker.isEventValid("string", ""));
        assert(eventWorker.isEventValid("string", "string"));
    }

    @Test
    public void getDataWithRetryInterval() {
        String host = "host-1";
        String body = "body-1";
        LogEvent event = Mockito.mock(LogEvent.class);
        when(event.getEndpointUrl()).thenReturn(host);
        when(event.getBody()).thenReturn(body);

        Data data = EventWorker.getData(event, 123L);
        assertEquals(eventWorker.getUrlFromInputData(data), host);
        assertEquals(eventWorker.getEventBodyFromInputData(data), body);
        assertEquals(eventWorker.getRetryIntervalFromInputData(data), 123);

        data = EventWorker.getData(event, -1L);
        assertEquals(eventWorker.getUrlFromInputData(data), host);
        assertEquals(eventWorker.getEventBodyFromInputData(data), body);
        assertEquals(eventWorker.getRetryIntervalFromInputData(data), -1);

        // compressed data

        body = makeLongString(20000);
        when(event.getBody()).thenReturn(body);

        data = EventWorker.getData(event, 123L);
        assertEquals(eventWorker.getUrlFromInputData(data), host);
        assertEquals(eventWorker.getEventBodyFromInputData(data), body);
        assertEquals(eventWorker.getRetryIntervalFromInputData(data), 123);

        data = EventWorker.getData(event, 0L);
        assertEquals(eventWorker.getUrlFromInputData(data), host);
        assertEquals(eventWorker.getEventBodyFromInputData(data), body);
        assertEquals(eventWorker.getRetryIntervalFromInputData(data), -1);

        data = EventWorker.getData(event, 0L);
        assertEquals(eventWorker.getUrlFromInputData(data), host);
        assertEquals(eventWorker.getEventBodyFromInputData(data), body);
        assertEquals(eventWorker.getRetryIntervalFromInputData(data), -1);
    }

    // Helpers

    private String makeLongString(int maxSize) {
        StringBuilder builder = new StringBuilder();
        String str = "random-string";
        int repeat = (maxSize / str.length()) + 1;
        for (int i=0; i<repeat; i++) builder.append(str);
        return builder.toString();
    }

}