raphael     2002/11/07 12:17:42

  Modified:    src/java/org/apache/jetspeed/capability
                        BaseCapabilityMap.java CapabilityMap.java
                        CapabilityMapFactory.java
  Added:       src/java/org/apache/jetspeed/capability
                        TestCapabilityMap.java
  Removed:     src/java/org/apache/jetspeed/capability
                        ClientCapabilityMap.java PortletCapabilityMap.java
  Log:
  Update capability package to use the configurable Client registry
  
  Revision  Changes    Path
  1.6       +110 -83   
jakarta-jetspeed/src/java/org/apache/jetspeed/capability/BaseCapabilityMap.java
  
  Index: BaseCapabilityMap.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-jetspeed/src/java/org/apache/jetspeed/capability/BaseCapabilityMap.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- BaseCapabilityMap.java    9 Apr 2002 22:13:59 -0000       1.5
  +++ BaseCapabilityMap.java    7 Nov 2002 20:17:41 -0000       1.6
  @@ -55,135 +55,162 @@
   package org.apache.jetspeed.capability;
   
   //standard Jetspeed stuff
  -import org.apache.jetspeed.util.*;
  -
  -//standard turbine stuff
  -import org.apache.turbine.util.*;
  +import org.apache.jetspeed.util.MimeType;
  +import org.apache.jetspeed.om.registry.ClientEntry;
  +import org.apache.jetspeed.om.registry.MediaTypeEntry;
  +import org.apache.jetspeed.services.Registry;
   
   //standard Java stuff
  -import java.util.*;
  +import java.util.Vector;
  +import java.util.Iterator;
   
   /**
  -
  -@author <a href="mailto:raphael@;apache.org">Rapha�l Luta</a>
  -@version $Id$
  -*/
  -public abstract class BaseCapabilityMap implements CapabilityMap {
  + * Read only wrapper around a ClientEntry registry entry that
  + * implements the CapabilityMap interface
  + *
  + * @author <a href="mailto:raphael@;apache.org">Rapha�l Luta</a>
  + * @version $Id$
  + */
  +public class BaseCapabilityMap implements CapabilityMap
  +{
   
       private String      useragent;
  -    private int         version = 0;
  -    private MimeType    mimeType = MimeType.HTML;
  -    private MimeType[]  mimeTypes = { mimeType };
  -    private Hashtable   capas = new Hashtable();
  +    private ClientEntry entry;
   
  -    /**
  -    Set the preferred MIME type for the current user-agent
  -    */
  -    public void setPreferredType( MimeType mimeType ) {
  -        this.mimeType = mimeType;
  +    protected BaseCapabilityMap(String agent, ClientEntry entry)
  +    {
  +        this.useragent = agent;
  +        this.entry = entry;
       }
  -     
  +
       /**
       @see CapabilityMap#getPreferredType
       */
  -    public MimeType getPreferredType() {
  -        return this.mimeType;
  -    }
  -    
  -    /**
  -    @see CapabilityMap#setAgent
  -    */
  -    public void setAgent( String useragent ) {
  -        this.useragent = useragent;
  +    public MimeType getPreferredType()
  +    {
  +        return entry.getMimetypeMap().getPreferredMimetype();
       }
  -     
  +
       /**
       @see CapabilityMap#getAgent
       */
  -    public String getAgent() {
  +    public String getAgent()
  +    {
           return this.useragent;
       }
  -     
  +
       /**
  -    @see CapabilityMap#setCapability
  +    @see CapabilityMap#hasCapability
       */
  -    public void setCapability( int cap ) {
  -        capas.put( new Integer( cap ) , "1");
  +    public boolean hasCapability( int cap )
  +    {
  +        return false;
       }
  -     
  +
       /**
       @see CapabilityMap#hasCapability
       */
  -    public boolean hasCapability( int cap ) {
  -        return ( capas.get( new Integer( cap ) ) != null );
  +    public boolean hasCapability( String capability )
  +    {
  +        Iterator i = entry.getCapabilityMap().getCapabilities();
  +
  +        while (i.hasNext())
  +        {
  +            String cap = (String)i.next();
  +
  +            if (cap.equals(capability))
  +            {
  +                return true;
  +            }
  +        }
  +
  +        return false;
       }
   
       /**
       @see CapabilityMap#getMimeTypes
       */
  -    public MimeType[] getMimeTypes() {
  -        return this.mimeTypes;
  -    }
  +    public MimeType[] getMimeTypes()
  +    {
  +        Vector v = new Vector();
  +        Iterator i = entry.getMimetypeMap().getMimetypes();
   
  -    /**
  -    @see CapabilityMap#setMimeTypes
  -    */
  -    public void setMimeTypes( MimeType[] mimeTypes ) {
  -        this.mimeTypes = mimeTypes;
  -        if (this.mimeTypes.length > 0)
  +        while (i.hasNext())
           {
  -            this.mimeType= this.mimeTypes[0];
  +            MimeType mime = (MimeType)i.next();
  +            v.add(mime);
           }
  -    }
  -    
  -    /**
  -    @see CapabilityMap#setMimeTypes
  -    */
  -    public void setMimeTypes( MimeType mimeType ) {
  -        this.mimeType= mimeType;
  -        this.mimeTypes = new MimeType[1];
  -        this.mimeTypes[0] = mimeType;
  +
  +        return (MimeType[])v.toArray();
       }
   
       /**
       @see CapabilityMap#supportsMimeType
       */
  -    public boolean supportsMimeType( MimeType mimeType ) {
  -        
  -        for( int i = 0; i < this.mimeTypes.length; ++i ) {
  -            
  -            if ( mimeTypes[i].equals( mimeType ) ) {
  +    public boolean supportsMimeType( MimeType mimeType )
  +    {
  +        Iterator i = entry.getMimetypeMap().getMimetypes();
  +
  +        while (i.hasNext())
  +        {
  +            MimeType mime = (MimeType)i.next();
  +
  +            if (mime.equals(mimeType))
  +            {
                   return true;
               }
  -            
           }
  -        
  +
           return false;
  -        
  +
  +    }
  +
  +    /**
  +    @see CapabilityMap#supportsMimeType
  +    */
  +    public boolean supportsMediaType( String media )
  +    {
  +        if (media == null)
  +        {
  +            return true;
  +        }
  +
  +        MediaTypeEntry mte = (MediaTypeEntry)Registry.getEntry(Registry.MEDIA_TYPE, 
media);
  +
  +        if (!supportsMimeType(new MimeType(mte.getMimeType())))
  +        {
  +            return false;
  +        }
  +
  +        return entry.getCapabilityMap().containsAll(mte.getCapabilityMap());
  +
       }
  -    
  +
       /**
       Create a map string representation
       */
  -    public String toString() {
  -        StringBuffer desc = new StringBuffer();
  -        
  -        MimeType[] mts = this.getMimeTypes();
  -        
  -        for( int i = 0; i < mts.length; ++i ) {
  -            desc.append( mts[i] ).append("-");            
  -        }
  -
  -        
  -        Enumeration en = capas.keys();
  -        
  -        while ( en.hasMoreElements() ) {
  -          String key = en.nextElement().toString();
  -          desc.append( key ).append( "/" );
  +    public String toString()
  +    {
  +        StringBuffer desc = new StringBuffer(entry.getName());
  +
  +        Iterator i = entry.getMimetypeMap().getMimetypes();
  +
  +        while (i.hasNext())
  +        {
  +            MimeType mime = (MimeType)i.next();
  +            desc.append( mime ).append("-");
           }
  -        
  +
  +        i = entry.getCapabilityMap().getCapabilities();
  +
  +        while ( i.hasNext() )
  +        {
  +          String capa = (String)i.next();
  +          desc.append(capa).append("/");
  +        }
  +
           return desc.toString();
       }
  -    
  +
   }
   
  
  
  
  1.5       +26 -36    
jakarta-jetspeed/src/java/org/apache/jetspeed/capability/CapabilityMap.java
  
  Index: CapabilityMap.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-jetspeed/src/java/org/apache/jetspeed/capability/CapabilityMap.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- CapabilityMap.java        24 Sep 2001 09:39:48 -0000      1.4
  +++ CapabilityMap.java        7 Nov 2002 20:17:41 -0000       1.5
  @@ -59,17 +59,15 @@
   import java.util.*;
   
   /**
  -This class describes various browsers capabilities and provides the
  -ability to query them.
  -
  -FIXME: the implementation should change to be configuration file based and
  -handle more browsers.
  -
  -@author <a href="mailto:raphael@;apache.org">Rapha�l Luta</a>
  -@author <a href="mailto:burton@;apache.org">Kevin A. Burton</a>
  -@version $Id$
  -*/
  -public interface CapabilityMap {
  + * This interface provides lookup features on the capabilities supported
  + * by a client user agent.
  + *
  + * @author <a href="mailto:raphael@;apache.org">Rapha�l Luta</a>
  + * @author <a href="mailto:burton@;apache.org">Kevin A. Burton</a>
  + * @version $Id$
  + */
  +public interface CapabilityMap
  +{
   
       /** Handle HTML Table */
       public static final int HTML_TABLE = 0;
  @@ -115,60 +113,52 @@
   
       /** Handle DOM */
       public static final int HTML_DOM = 16;
  -    
  +
       /**
       Returns the preferred MIME type for the current user-agent
       */
       public MimeType getPreferredType();
   
       /**
  -    */
  -    public void setPreferredType( MimeType mimeType );
  -     
  -    /**
       Returns the user-agent string
       */
       public String getAgent();
   
       /**
  -    Sets the user-agent string
  -    */
  -    public void setAgent( String agent );
  -    
  -    /**
       Checks to see if the current agent has the specified capability
       */
       public boolean hasCapability( int cap );
   
       /**
  -    Set a capability for the current agent
  +    Checks to see if the current agent has the specified capability
       */
  -    public void setCapability( int cap );
  -    
  +    public boolean hasCapability( String capability );
  +
       /**
       Get the mime types that this CapabilityMap supports.
       */
       public MimeType[] getMimeTypes();
   
       /**
  -    Set the mime types that this CapabilityMap supports
  -    */
  -    public void setMimeTypes( MimeType[] mimeTypes );
  -
  -    /**    
  -    @see #setMimeTypes( MimeType[] )
  -    */
  -    public void setMimeTypes( MimeType mimeType );
  -
  -    /**
       Return true if this CapabilityMap supports the given MimeType
       */
       public boolean supportsMimeType( MimeType mimeType );
  -    
  +
  +    /**
  +     * Return true if this CapabilityMap supports the given media type
  +     *
  +     * @param media the name of a media type registered in the
  +     * MediaType regsitry
  +     *
  +     * @return true is the capabilities of this agent at least match those
  +     * required by the media type
  +     */
  +    public boolean supportsMediaType( String media );
  +
       /**
       Create a map -> string representation
       */
       public String toString();
  -    
  +
   }
   
  
  
  
  1.10      +46 -63    
jakarta-jetspeed/src/java/org/apache/jetspeed/capability/CapabilityMapFactory.java
  
  Index: CapabilityMapFactory.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-jetspeed/src/java/org/apache/jetspeed/capability/CapabilityMapFactory.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- CapabilityMapFactory.java 9 Apr 2002 22:13:59 -0000       1.9
  +++ CapabilityMapFactory.java 7 Nov 2002 20:17:41 -0000       1.10
  @@ -54,9 +54,15 @@
   
   package org.apache.jetspeed.capability;
   
  -import org.apache.turbine.util.RunData;
   import java.util.Hashtable;
   
  +import org.apache.jetspeed.om.registry.ClientEntry;
  +import org.apache.jetspeed.om.registry.ClientRegistry;
  +import org.apache.jetspeed.services.Registry;
  +
  +import org.apache.turbine.util.RunData;
  +import org.apache.turbine.util.Log;
  +
   /**
   This class describes various browsers capabilities and provides the
   ability to query them.
  @@ -67,91 +73,68 @@
   @author <a href="mailto:raphael@;apache.org">Rapha�l Luta</a>
   @version $Id$
   */
  -public class CapabilityMapFactory {
  +public class CapabilityMapFactory
  +{
   
       public static final String DEFAULT_AGENT = "Mozilla/4.0";
  -    public static final String AGENT_XML = "agentxml/1.0";
  -
  -    private static Hashtable agents = new Hashtable();    
  -
  -    private static final CapabilityMap DEFAULT_CAPABILITY_MAP = 
  -        new ClientCapabilityMap( DEFAULT_AGENT );
  -    
  -
  -    /**
  -    Name for CapabilityMap fetch constant.
  -    */
  -    public static final String CAPABILITY_MAP = "jetspeed.capability-map";
   
  +    public static final String AGENT_XML = "agentxml/1.0";
   
       /**
       Returns the map corresponding to the given RunData.
       *
       FIXME: the method will be changed to use a Jetspeed specific request
       wrapper
  -    
  +
       @param rundata the request RunData
       @return the map correspondin to the user-agent described in the RunData
       */
  -    public static CapabilityMap getCapabilityMap( RunData rundata ) {
  +    public static CapabilityMap getCapabilityMap( RunData rundata )
  +    {
   
  -        if ( ( rundata == null ) || ( rundata.getRequest() == null ) ) {
  -            return getDefaultCapabilityMap();
  +        if (rundata == null)
  +        {
  +            return getCapabilityMap(DEFAULT_AGENT);
           }
   
  -        /*
  -        If a session has been created. store this CapabilityMap into the session 
  -        for additional performance.
  -        */
  -
  -        CapabilityMap cm = null;
  -        // SGP: Tomcat gives a nullpointer exception sometimes
  -        // unless we ask first if the session is valid.
  -        if ( rundata.getRequest().isRequestedSessionIdValid() && ! 
rundata.getSession().isNew() ) {
  -            cm = (CapabilityMap)rundata.getSession().getValue( CAPABILITY_MAP );
  -        }
  -
  -        if ( cm == null ) {
  -
  -            String userAgent = rundata.getUserAgent();
  -
  -            cm = getCapabilityMap( userAgent );
  -
  -            /*
  -            If a session has been created. store this CapabilityMap into the 
session 
  -            for additional performance.
  -            */
  -            if ( rundata.getRequest().isRequestedSessionIdValid() &&
  -                 ! rundata.getSession().isNew() ) {
  -                rundata.getSession().putValue( CAPABILITY_MAP, cm );
  -            }
  -
  -        }
  -        
  -        return cm;
  +        return getCapabilityMap( rundata.getUserAgent() );
       }
  -    
  +
       /**
       Returns the map corresponding to the given user-agent
  -    
  +
       @param useragent a user-agent string in the HTTP User-agent format
       @return the map corresponding to the user-agent
       */
  -    public static CapabilityMap getCapabilityMap( String useragent ) {
  -
  -        if ( useragent == null ) return getDefaultCapabilityMap();
  +    public static CapabilityMap getCapabilityMap( String useragent )
  +    {
  +        CapabilityMap map = null;
  +
  +        if (useragent == null)
  +        {
  +            useragent = DEFAULT_AGENT;
  +        }
   
  -        CapabilityMap map = (CapabilityMap)agents.get( useragent );        
  -        if ( map == null ) {
  -            map = new ClientCapabilityMap( useragent );
  -            agents.put( useragent, map );
  +        ClientRegistry registry = (ClientRegistry)Registry.get(Registry.CLIENT);
  +        ClientEntry entry = registry.findEntry(useragent);
  +        map = new BaseCapabilityMap(useragent, entry);
  +
  +        if (Log.getLogger().isDebugEnabled())
  +        {
  +            Log.debug("CapabilityMap: User-agent: "+useragent+" mapped to "+map);
           }
   
  -        return map;    
  +        return map;
       }
   
  -    public static CapabilityMap getDefaultCapabilityMap() {
  -        return DEFAULT_CAPABILITY_MAP;
  -    }
  -}
  +    /**
  +    Returns the map corresponding to the given user-agent
   
  +    @param useragent a user-agent string in the HTTP User-agent format
  +    @return the map corresponding to the user-agent
  +    */
  +    public static CapabilityMap getDefaultCapabilityMap()
  +    {
  +        return getCapabilityMap(DEFAULT_AGENT);
  +    }
  +}
  \ No newline at end of file
  
  
  
  1.1                  
jakarta-jetspeed/src/java/org/apache/jetspeed/capability/TestCapabilityMap.java
  
  Index: TestCapabilityMap.java
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" and
   *     "Apache Jetspeed" must not be used to endorse or promote products
   *    derived from this software without prior written permission. For
   *    written permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache" or
   *    "Apache Jetspeed", nor may "Apache" appear in their name, without
   *    prior written permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.jetspeed.capability;
  
  // Junit imports
  import junit.framework.Test;
  import junit.framework.TestSuite;
  import junit.framework.TestCase;
  
  import org.apache.turbine.util.TurbineConfig;
  import org.apache.turbine.util.StringUtils;
  import org.apache.jetspeed.capability.CapabilityMapFactory;
  import org.apache.jetspeed.capability.CapabilityMap;
  import org.apache.jetspeed.services.Registry;
  import org.apache.jetspeed.om.registry.ClientRegistry;
  import org.apache.jetspeed.om.registry.ClientEntry;
  import org.apache.jetspeed.util.MimeType;
  
  /**
   * Unit test for capbility package
   *
   * @author <a href="[EMAIL PROTECTED]">Rapha�l Luta</a>
   * @version $Id: TestCapabilityMap.java,v 1.1 2002/11/07 20:17:41 raphael Exp $
   */
  
  public class TestCapabilityMap extends TestCase
  {
  
      /**
       * Defines the testcase name for JUnit.
       *
       * @param name the testcase's name.
       */
      public TestCapabilityMap( String name )
      {
          super( name );
      }
  
      /**
       * Start the tests.
       *
       * @param args the arguments. Not used
       */
      public static void main(String args[])
      {
          junit.awtui.TestRunner.main( new String[] { 
TestCapabilityMap.class.getName() } );
      }
  
      public void setup()
      {
          System.out.println("Setup: Testing CapabilityMap functionality");
      }
      /**
       * Creates the test suite.
       *
       * @return a test suite (<code>TestSuite</code>) that includes all methods
       *         starting with "test"
       */
      public static Test suite()
      {
          // All methods starting with "test" will be executed in the test suite.
          return new TestSuite( TestCapabilityMap.class );
      }
  
      public void testRegistry() throws Exception
      {
          try
          {
              // Make sure the Registry works
              ClientRegistry cr = (ClientRegistry)Registry.get(Registry.CLIENT);
              ClientEntry ce = (ClientEntry)cr.getEntry("ie5");
              assertNotNull(ce);
          }
          catch (Exception e)
          {
              String errmsg = "Error in test: " + e.toString();
             // e.printStackTrace();
             assertNotNull(errmsg, null);
          }
      }
  
      public void testDefaultMap() throws Exception
      {
          try
          {
              // first test default capailitymap
              CapabilityMap cm = CapabilityMapFactory.getDefaultCapabilityMap();
              assertNotNull(cm);
              assertTrue(cm.toString().startsWith("ns4"));
          }
          catch (Exception e)
          {
              String errmsg = "Error in test: " + e.toString();
             // e.printStackTrace();
             assertNotNull(errmsg, null);
          }
      }
  
      public void testStandardMap() throws Exception
      {
          try
          {
              // then test different standard browsers
              getUserAgent("ie5","Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)");
              getUserAgent("ns4","Mozilla/4.78 (Windows 2000; U) Opera 6.01  [fr]");
              getUserAgent("mozilla","Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; 
rv:1.0rc1) Gecko/20020417");
              getUserAgent("nokia_generic","Nokia3330/1.0 (03.05)");
              getUserAgent("lynx","Lynx/2.8.4rel.1 libwww-FM/2.14 SSL-MM/1.4.1 
OpenSSL/0.9.6b");
          }
          catch (Exception e)
          {
              String errmsg = "Error in test: " + e.toString();
             // e.printStackTrace();
             assertNotNull(errmsg, null);
          }
      }
  
      public void testCapabilityCheck() throws Exception
      {
          try
          {
              // test simple capabilities
              CapabilityMap cm = CapabilityMapFactory.getCapabilityMap("Mozilla/5.0 
(Windows; U; Windows NT 5.0; en-US; rv:1.0rc1) Gecko/20020417");
              assertTrue(cm.hasCapability("HTML_DOM_1"));
              assertTrue(cm.hasCapability("HTML_IFRAME"));
              assertTrue(cm.supportsMimeType(MimeType.XML));
              assertTrue(cm.supportsMediaType("html"));
          }
          catch (Exception e)
          {
              String errmsg = "Error in test: " + e.toString();
             // e.printStackTrace();
             assertNotNull(errmsg, null);
          }
      }
  
      private void getUserAgent(String name, String ua)
      {
          CapabilityMap cm = CapabilityMapFactory.getCapabilityMap(ua);
          assertNotNull(cm);
          assertNotNull(name);
          assertTrue(cm.toString().startsWith(name));
      }
  
      /*
        Configuration object to run Turbine outside a servlet container
        ( uses turbine.properties )
      */
      private static TurbineConfig config = null;
  
      /*
        Sets up TurbineConfig using the system property:
        <pre>turbine.properties</pre>
      */
      static
      {
          try
          {
             config = new TurbineConfig( "../webapp", 
"/WEB-INF/conf/TurbineResources.properties");
             config.init();
          }
          catch (Exception e)
          {
              fail(StringUtils.stackTrace(e));
          }
      }
  }
  
  
  

--
To unsubscribe, e-mail:   <mailto:jetspeed-dev-unsubscribe@;jakarta.apache.org>
For additional commands, e-mail: <mailto:jetspeed-dev-help@;jakarta.apache.org>

Reply via email to