Coverage Report - groovyx.net.http.ParserRegistry
 
Classes in this File Line Coverage Branch Coverage Complexity
ParserRegistry
69%
27/39
58%
7/12
0
 
 1  
 /*
 2  
  * Copyright 2003-2008 the original author or authors.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  *
 16  
  * You are receiving this code free of charge, which represents many hours of
 17  
  * effort from other individuals and corporations.  As a responsible member 
 18  
  * of the community, you are asked (but not required) to donate any 
 19  
  * enhancements or improvements back to the community under a similar open 
 20  
  * source license.  Thank you. -TMN
 21  
  */
 22  
 package groovyx.net.http;
 23  
 
 24  
 import groovy.lang.Closure;
 25  
 import groovy.util.XmlSlurper;
 26  
 import groovy.util.slurpersupport.GPathResult;
 27  
 import groovyx.net.http.HTTPBuilder.SendDelegate;
 28  
 
 29  
 import java.io.IOException;
 30  
 import java.io.InputStream;
 31  
 import java.io.InputStreamReader;
 32  
 import java.io.Reader;
 33  
 import java.io.UnsupportedEncodingException;
 34  
 import java.nio.charset.Charset;
 35  
 import java.util.HashMap;
 36  
 import java.util.List;
 37  
 import java.util.Map;
 38  
 
 39  
 import javax.xml.parsers.ParserConfigurationException;
 40  
 
 41  
 import net.sf.json.JSON;
 42  
 import net.sf.json.groovy.JsonSlurper;
 43  
 
 44  
 import org.apache.commons.logging.Log;
 45  
 import org.apache.commons.logging.LogFactory;
 46  
 import org.apache.http.HttpResponse;
 47  
 import org.apache.http.NameValuePair;
 48  
 import org.apache.http.client.utils.URLEncodedUtils;
 49  
 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 50  
 import org.codehaus.groovy.runtime.MethodClosure;
 51  
 import org.cyberneko.html.parsers.SAXParser;
 52  
 import org.xml.sax.SAXException;
 53  
 
 54  
 
 55  
 /**
 56  
  * Keeps track of response parsers for each content type.  Each parser 
 57  
  * should should be a closure that accepts an {@link HttpResponse} instance,
 58  
  * and returns whatever handler is appropriate for reading the response 
 59  
  * data for that content-type.  For example, a plain-text response should 
 60  
  * probably be parsed with a <code>Reader</code>, while an XML response 
 61  
  * might be parsed by an XmlSlurper, which would then be passed to the 
 62  
  * response closure. 
 63  
  * @see ContentType
 64  
  */
 65  15
 public class ParserRegistry {
 66  
         
 67  15
         protected Closure defaultParser = new MethodClosure( this, "parseStream" );
 68  15
         protected final Log log = LogFactory.getLog( getClass() );
 69  
         
 70  
         /**
 71  
          * Get the charset from the response
 72  
          * @param resp
 73  
          */
 74  
         protected String getCharset( HttpResponse resp ) {
 75  9
                 return resp.getEntity().getContentType()
 76  
                         .getElements()[0].getParameterByName("charset").getValue();
 77  
         }
 78  
         
 79  
         /**
 80  
          * Get the content-type string from the response (no charset)
 81  
          * @param resp
 82  
          */
 83  
         protected String getContentType( HttpResponse resp ) {
 84  9
                 return resp.getEntity().getContentType()
 85  
                         .getElements()[0].getName();
 86  
         }
 87  
         
 88  
         /**
 89  
          * Default parser used for binary data.
 90  
          * @param resp
 91  
          * @return an InputStream 
 92  
          * @throws IllegalStateException
 93  
          * @throws IOException
 94  
          */
 95  
         public InputStream parseStream( HttpResponse resp ) throws IOException {
 96  0
                 return resp.getEntity().getContent();
 97  
         }
 98  
         
 99  
         /**
 100  
          * Default parser used to handle plain text data.  The response text 
 101  
          * is decoded using the charset passed in the response content-type 
 102  
          * header. 
 103  
          * @param resp
 104  
          * @return
 105  
          * @throws UnsupportedEncodingException
 106  
          * @throws IllegalStateException
 107  
          * @throws IOException
 108  
          */
 109  
         public Reader parseText( HttpResponse resp ) throws IOException {
 110  9
                 String charset = getCharset( resp );
 111  9
                 if ( charset == null || charset.trim().equals("") )
 112  0
                         charset = Charset.defaultCharset().name();
 113  9
                 return new InputStreamReader( resp.getEntity().getContent(), charset );
 114  
         }
 115  
         
 116  
         /**
 117  
          * Default parser used to decode a URL-encoded response.
 118  
          * @param resp
 119  
          * @return
 120  
          * @throws IOException
 121  
          */
 122  
         public Map<String,String> parseForm( HttpResponse resp ) throws IOException {
 123  0
                 List<NameValuePair> params = URLEncodedUtils.parse( resp.getEntity() );
 124  0
                 Map<String,String> paramMap = new HashMap<String,String>(params.size());
 125  0
                 for ( NameValuePair param : params ) 
 126  0
                         paramMap.put( param.getName(), param.getValue() );
 127  0
                 return paramMap;
 128  
         }
 129  
         
 130  
         /**
 131  
          * Parse an HTML document by passing it through the NekoHTML parser.
 132  
          * @see SAXParser
 133  
          * @see XmlSlurper#parse(Reader)
 134  
          * @param resp HTTP response from which to parse content
 135  
          * @return the {@link GPathResult} from calling {@link XmlSlurper#parse(Reader)}
 136  
          * @throws IOException
 137  
          * @throws SAXException
 138  
          */
 139  
         public GPathResult parseHTML( HttpResponse resp ) throws IOException, SAXException {
 140  6
                 return new XmlSlurper( new org.cyberneko.html.parsers.SAXParser() )
 141  
                         .parse( parseText( resp ) );
 142  
         }
 143  
         
 144  
         /**
 145  
          * Default parser used to decode an XML response.  
 146  
          * @see XmlSlurper#parse(Reader)
 147  
          * @param resp HTTP response from which to parse content
 148  
          * @return the {@link GPathResult} from calling {@link XmlSlurper#parse(Reader)}
 149  
          * @throws IOException
 150  
          * @throws SAXException
 151  
          * @throws ParserConfigurationException
 152  
          */
 153  
         public GPathResult parseXML( HttpResponse resp ) throws IOException, SAXException, ParserConfigurationException {
 154  0
                 return new XmlSlurper().parse( parseText( resp ) );
 155  
         }
 156  
         
 157  
         /**
 158  
          * Default parser used to decode a JSON response.
 159  
          * @param resp
 160  
          * @return
 161  
          * @throws IOException
 162  
          */
 163  
         public JSON parseJSON( HttpResponse resp ) throws IOException {
 164  
                 // there is a bug in the JsonSlurper.parse method...
 165  3
                 String jsonTxt = DefaultGroovyMethods.getText( parseText( resp ) );                        
 166  3
                 return new JsonSlurper().parseText( jsonTxt );
 167  
         }
 168  
         
 169  15
         protected Map<String,Closure> registeredParsers = buildDefaultParserMap();
 170  
         
 171  
         /**
 172  
          * Register a new parser for the given content-type.  The parser
 173  
          * should accept an HttpResponse argument and return a type suitable
 174  
          * to be passed to a {@link SendDelegate#getResponse() response handler}.
 175  
          * @param contentType
 176  
          * @param closure
 177  
          */
 178  
         public void register( String contentType, Closure closure ) {
 179  0
                 registeredParsers.put( contentType, closure );
 180  0
         }
 181  
         
 182  
         /* Retrieve a parser for the given response content-type string.  This
 183  
          * should usually not be called by a user.  The appropriate parser will
 184  
          * be resolved prior to executing the response handler. 
 185  
          * @param contentType
 186  
          * @return parser that can interpret the given response content type,
 187  
          *   or null if no parser is registered for the given content-type
 188  
          */
 189  
         Closure get( String contentType ) { 
 190  9
                 Closure parser = registeredParsers.get(contentType);
 191  9
                 if ( parser == null ) {
 192  0
                         log.warn( "Cannot find parser for content-type: " + contentType 
 193  
                                         + " -- using default parser.");
 194  0
                         parser = defaultParser;
 195  
                 }
 196  9
                 return parser;
 197  
         }
 198  
         
 199  
         /**
 200  
          * Returns a map of default parsers.  Override this method to change 
 201  
          * what parsers are registered by default.  You can of course call
 202  
          * <code>super.buildDefaultParserMap()</code> and then add or remove 
 203  
          * from that result as well.
 204  
          */
 205  
         protected Map<String,Closure> buildDefaultParserMap() {
 206  15
                 Map<String,Closure> parsers = new HashMap<String,Closure>();
 207  
                 
 208  15
                 parsers.put( ContentType.BINARY.toString(), new MethodClosure( this, "parseStream" ) );
 209  15
                 parsers.put( ContentType.TEXT.toString(), new MethodClosure(this,"parseText") );
 210  15
                 parsers.put( ContentType.URLENC.toString(), new MethodClosure(this,"parseForm") );
 211  15
                 parsers.put( ContentType.HTML.toString(), new MethodClosure(this,"parseHTML") );
 212  
                 
 213  15
                 Closure pClosure = new MethodClosure(this,"parseXML");
 214  60
                 for ( String ct : ContentType.XML.getContentTypeStrings() )
 215  45
                         parsers.put( ct, pClosure );
 216  
                 
 217  15
                 pClosure = new MethodClosure(this,"parseJSON");
 218  60
                 for ( String ct : ContentType.JSON.getContentTypeStrings() )
 219  45
                         parsers.put( ct, pClosure );
 220  
                 
 221  15
                 return parsers;
 222  
         }
 223  
 }