You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2012/01/27 20:18:57 UTC

svn commit: r1236847 - in /incubator/jena/Jena2/ARQ/trunk: ./ src/main/java/org/openjena/atlas/io/ src/main/java/org/openjena/atlas/web/ src/main/java/org/openjena/riot/web/ src/test/java/org/openjena/atlas/ src/test/java/org/openjena/atlas/web/

Author: andy
Date: Fri Jan 27 19:18:56 2012
New Revision: 1236847

URL: http://svn.apache.org/viewvc?rev=1236847&view=rev
Log:
Tidying up.

Added:
    incubator/jena/Jena2/ARQ/trunk/D.nt
    incubator/jena/Jena2/ARQ/trunk/Q.rq
    incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/web/AcceptList.java
    incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/web/
    incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/web/TS_Web.java
    incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/web/TestContentNegotiation.java
Modified:
    incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/io/BufferingWriter.java
    incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/web/WebLib.java
    incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/riot/web/HttpOp.java
    incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/TC_Atlas.java

Added: incubator/jena/Jena2/ARQ/trunk/D.nt
URL: http://svn.apache.org/viewvc/incubator/jena/Jena2/ARQ/trunk/D.nt?rev=1236847&view=auto
==============================================================================
    (empty)

Added: incubator/jena/Jena2/ARQ/trunk/Q.rq
URL: http://svn.apache.org/viewvc/incubator/jena/Jena2/ARQ/trunk/Q.rq?rev=1236847&view=auto
==============================================================================
--- incubator/jena/Jena2/ARQ/trunk/Q.rq (added)
+++ incubator/jena/Jena2/ARQ/trunk/Q.rq Fri Jan 27 19:18:56 2012
@@ -0,0 +1,3 @@
+PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+
+SELECT ( rdf:type as ?X ) {}
\ No newline at end of file

Modified: incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/io/BufferingWriter.java
URL: http://svn.apache.org/viewvc/incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/io/BufferingWriter.java?rev=1236847&r1=1236846&r2=1236847&view=diff
==============================================================================
--- incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/io/BufferingWriter.java (original)
+++ incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/io/BufferingWriter.java Fri Jan 27 19:18:56 2012
@@ -119,7 +119,7 @@ public final class BufferingWriter exten
 //    }
 
     /** Create a buffering output stream of charcaters to a {@link org.openjena.atlas.lib.Sink} */
-    /*public*/private BufferingWriter(Sink<ByteBuffer> sink) { this(sink, SIZE, BLOB_SIZE) ; }
+    public BufferingWriter(Sink<ByteBuffer> sink) { this(sink, SIZE, BLOB_SIZE) ; }
     
     /** Create a buffering output stream of charcaters to a {@link org.openjena.atlas.lib.Sink} */
     /*package*/ BufferingWriter(Sink<ByteBuffer> sink, int size, int blobSize)

Added: incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/web/AcceptList.java
URL: http://svn.apache.org/viewvc/incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/web/AcceptList.java?rev=1236847&view=auto
==============================================================================
--- incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/web/AcceptList.java (added)
+++ incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/web/AcceptList.java Fri Jan 27 19:18:56 2012
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openjena.atlas.web;
+
+import java.util.* ;
+
+import org.openjena.atlas.logging.Log ;
+import org.openjena.atlas.web.MediaRange ;
+import org.openjena.atlas.web.MediaType ;
+
+public class AcceptList
+{
+    private List<MediaRange> ranges ;
+    /** 
+     * Create an empty list of accept items from the give strings.
+     * @param acceptStrings
+     */
+    
+    private AcceptList()
+    { ranges = new ArrayList<MediaRange>() ; }
+
+    /**
+     * Create a list of accept items from the give strings.
+     * @param mediaRanges
+     */
+    
+    public AcceptList(List<MediaRange> mediaRanges)
+    { ranges = new ArrayList<MediaRange>(mediaRanges) ; }
+    
+    /**
+     * Create a list of accept items from the give MediaTypes.
+     * @param acceptItems
+     */
+    
+    public AcceptList(MediaRange...acceptItems)
+    { ranges = Arrays.asList(acceptItems) ; }
+    
+    /**
+     * Create a list of accept items from the give MediaTypes.
+     * @param acceptItems
+     */
+    
+    public AcceptList(MediaType...acceptItems)
+    { 
+        ranges = new ArrayList<MediaRange>() ;
+        for ( MediaType mtype : acceptItems )
+            ranges.add(new MediaRange(mtype)) ;
+    }        
+
+    /**
+     * Create a list of accept items from strings.
+     * @param acceptStrings
+     */
+    
+    public AcceptList(String... acceptStrings)
+    {
+        ranges = new ArrayList<MediaRange>() ;
+        for ( int i = 0 ; i < acceptStrings.length ; i++ )
+            ranges.add(new MediaRange(acceptStrings[i])) ;
+    }
+    
+    /**
+     * Parse an HTTP Accept (or etc) header string. 
+     * @param headerString
+     */
+    
+    public AcceptList(String headerString)
+    {
+        try {
+            ranges = stringToAcceptList(headerString) ;
+        } catch (Exception ex)
+        {
+            ex.printStackTrace(System.err) ;
+            Log.warn(this, "Unrecognized accept string (ignored): "+headerString) ;
+            ranges = new ArrayList<MediaRange>() ;
+        }
+    }
+    
+    private /*public*/ boolean accepts(MediaRange aItem)
+    {
+        return match(aItem) != null ;
+    }
+    
+    private List<MediaRange> entries()
+    {
+        return Collections.unmodifiableList(ranges) ;
+    }
+
+    private final static MediaRangeCompare comparator = new MediaRangeCompare() ;
+    
+    /** Find and return a match for a MediaRange */
+    public MediaRange match(MediaRange aItem)
+    {
+        // Normally aItem is an offer - a concrete media type.
+//        ensureSorted() ;
+        // Search all, find best by specifivity, "q"(quality), and then first occurring if otherwise equal.
+        
+        MediaRange choice = null ;
+        
+        for ( MediaRange acceptItem : ranges )
+        {
+            if ( acceptItem.accepts(aItem) )
+            {
+                // Return the more grounded term
+                // E.g. aItem = text/plain ; acceptItem = text/*
+                
+                if ( choice != null && choice.get_q() >= acceptItem.get_q() )
+                    continue ;
+                // Return the more grounded term
+                // E.g. aItem = text/plain ; acceptItem = text/*
+                // This looses any q
+                if ( aItem.moreGroundedThan(acceptItem) )
+                {
+                    // Clone.
+                    acceptItem = new MediaRange(acceptItem) ;
+                    // Copy type info 
+                    acceptItem.setType(aItem.getType()) ;
+                    acceptItem.setSubType(aItem.getSubType()) ;
+                }
+                choice = acceptItem ;
+            }
+        }
+        return choice ;
+    }
+ 
+//    private void ensureSorted()
+//    {
+//        // Need to record the position as well to 
+//        if ( sortedRanges == null )
+//        {
+//            sortedRanges = new ArrayList<MediaRange>(ranges) ; 
+//            Collections.sort(sortedRanges, comparator) ;
+//        }
+//    }
+    /** Find the best thing in offer list with the proposal 
+     *  "best" means highest q value, with left most being better for same q.
+     * 
+     * @param proposalList Client list of possibilities
+     * @param offerList    Server list of possibilities
+     * @return MediaType
+     */
+    
+    static public MediaType match(AcceptList proposalList, AcceptList offerList)
+    {
+        MediaRange choice = null ;  // From offerlist
+        //MediaRange choice2 = null ; // From proposal (q value and text/*)
+        
+        for ( MediaRange offer : offerList.entries() )
+        {
+            MediaRange m = proposalList.match(offer) ;
+            if ( m != null )
+            {
+                if ( choice != null && choice.get_q() >= m.get_q() )
+                    continue ; 
+                choice = m ;  
+            }
+        }
+        if ( choice == null )
+            return null ;
+        return new MediaType(choice);
+    }
+    
+    public MediaRange first()
+    {
+        MediaRange choice = null ;
+        for ( MediaRange acceptItem : ranges )
+        {
+            if ( choice != null && choice.get_q() >= acceptItem.get_q() )
+                continue ;
+            choice = acceptItem ;
+        }
+        return choice ;
+    }
+    
+    @Override
+    public String toString() { return ranges.toString() ; }
+    
+    private static List<MediaRange> stringToAcceptList(String s)
+    {
+        List<MediaRange> ranges = new ArrayList<MediaRange>() ;
+        if ( s == null )
+            return ranges ;
+
+        String[] x = s.split(",") ;
+        for ( int i = 0 ; i < x.length ; i++ )
+        {
+            if ( x[i].equals(""))
+                continue ;
+            MediaRange mType = new MediaRange(x[i]) ;
+            ranges.add(mType) ;
+        }
+        return ranges ;
+    }
+    
+    private static class MediaRangeCompare implements Comparator<MediaRange>
+    {
+        @Override
+        public int compare(MediaRange mType1, MediaRange mType2)
+        {
+            int r = Double.compare(mType1.get_q(), mType2.get_q()) ;
+            
+            if ( r == 0 )
+                r = subCompare(mType1.getType(), mType2.getType()) ;
+            
+            if ( r == 0 )
+                r = subCompare(mType1.getSubType(), mType2.getSubType()) ;
+            
+//            if ( r == 0 )
+//            {
+//                // This reverses the input order so that the rightmost elements is the
+//                // greatest and hence is the first mentioned in the accept range.
+//                
+//                if ( mType1.posn < mType2.posn )
+//                    r = +1 ;
+//                if ( mType1.posn > mType2.posn )
+//                    r = -1 ;
+//            }
+            
+            // The most significant sorts to the first in a list.
+            r = -r ;
+            return r ;
+        }
+        
+        public int subCompare(String a, String b)
+        {
+            if ( a == null )
+                return 1 ;
+            if ( b == null )
+                return -1 ;
+            if ( a.equals("*") && b.equals("*") )
+                return 0 ;
+            if ( a.equals("*") )
+                return -1 ;
+            if ( b.equals("*") )
+                return 1 ;
+            return 0 ;
+        }
+    }
+}

Modified: incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/web/WebLib.java
URL: http://svn.apache.org/viewvc/incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/web/WebLib.java?rev=1236847&r1=1236846&r2=1236847&view=diff
==============================================================================
--- incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/web/WebLib.java (original)
+++ incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/atlas/web/WebLib.java Fri Jan 27 19:18:56 2012
@@ -21,7 +21,7 @@ package org.openjena.atlas.web;
 public class WebLib
 {
     /** Split a string, removing whitespace around the split string.
-     * e.g. Use in splittign HTTP accept/content-type headers.  
+     * e.g. Use in splitting HTTP accept/content-type headers.  
      */
     public static String[] split(String s, String splitStr)
     {

Modified: incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/riot/web/HttpOp.java
URL: http://svn.apache.org/viewvc/incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/riot/web/HttpOp.java?rev=1236847&r1=1236846&r2=1236847&view=diff
==============================================================================
--- incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/riot/web/HttpOp.java (original)
+++ incubator/jena/Jena2/ARQ/trunk/src/main/java/org/openjena/riot/web/HttpOp.java Fri Jan 27 19:18:56 2012
@@ -271,31 +271,6 @@ public class HttpOp
         } finally { closeEntity(response.getEntity()) ; }
     }
     
-//    // Jena 2.6.4 bug.
-//    
-//    public static String readWholeFileAsUTF8(InputStream in) throws IOException
-//    {
-//        Reader r = new BufferedReader(FileUtils.asUTF8(in),1024) ;
-//        return readWholeFileAsUTF8(r) ;
-//    }
-//    
-//    
-//    // Private worker as we are trying to force UTF-8. 
-//    private static String readWholeFileAsUTF8(Reader r) throws IOException
-//    {
-//        StringWriter sw = new StringWriter(1024);
-//        char buff[] = new char[1024];
-//        int l ;
-//        while ( (l = r.read(buff)) != -1 ) {
-//            if (l <= 0)
-//                break;
-//            sw.write(buff, 0, l);
-//        }
-//        r.close();
-//        sw.close();
-//        return sw.toString();  
-//    }
-//    
 //    public static void main2(String...argv) throws Exception
 //    {
 //        String queryString =  "SELECT * { ?s ?p ?o } LIMIT 1" ;
@@ -324,7 +299,7 @@ public class HttpOp
 //        InputStream instream = entity.getContent() ;
 //        try {
 //            //entity = new BufferedHttpEntity(entity) ;
-//            String x = readWholeFileAsUTF8(instream) ;
+//            String x = FileUtils.readWholeFileAsUTF8(instream) ;
 //            System.out.print(x) ;  
 //        } finally {
 //            instream.close();

Modified: incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/TC_Atlas.java
URL: http://svn.apache.org/viewvc/incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/TC_Atlas.java?rev=1236847&r1=1236846&r2=1236847&view=diff
==============================================================================
--- incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/TC_Atlas.java (original)
+++ incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/TC_Atlas.java Fri Jan 27 19:18:56 2012
@@ -26,6 +26,7 @@ import org.openjena.atlas.io.TS_IO ;
 import org.openjena.atlas.iterator.TS_Iterator ;
 import org.openjena.atlas.json.TS_JSON ;
 import org.openjena.atlas.lib.TS_Lib ;
+import org.openjena.atlas.web.TS_Web ;
 
 @RunWith(Suite.class)
 @Suite.SuiteClasses( {
@@ -36,6 +37,7 @@ import org.openjena.atlas.lib.TS_Lib ;
     , TS_IO.class
     , TS_JSON.class
     , TS_Data.class
+    , TS_Web.class
 }) 
 
 public class TC_Atlas

Added: incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/web/TS_Web.java
URL: http://svn.apache.org/viewvc/incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/web/TS_Web.java?rev=1236847&view=auto
==============================================================================
--- incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/web/TS_Web.java (added)
+++ incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/web/TS_Web.java Fri Jan 27 19:18:56 2012
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openjena.atlas.web;
+
+import org.junit.runner.RunWith ;
+import org.junit.runners.Suite ;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses( {
+    TestContentNegotiation.class
+} )
+public class TS_Web
+{
+
+}
+

Added: incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/web/TestContentNegotiation.java
URL: http://svn.apache.org/viewvc/incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/web/TestContentNegotiation.java?rev=1236847&view=auto
==============================================================================
--- incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/web/TestContentNegotiation.java (added)
+++ incubator/jena/Jena2/ARQ/trunk/src/test/java/org/openjena/atlas/web/TestContentNegotiation.java Fri Jan 27 19:18:56 2012
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openjena.atlas.web;
+
+import org.junit.Test ;
+import org.openjena.atlas.junit.BaseTest ;
+
+public class TestContentNegotiation extends BaseTest
+{
+    static final String ctFirefox = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" ;
+    static final String ctIE_6  = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/msword, */*" ;
+    
+    static final String ctApplicationXML     =  "application/xml" ;
+    static final String ctApplicationRDFXML  =  "application/rdf+xml" ;
+    static final String ctApplicationStar    =  "application/*" ;
+    // Legal?? */xml
+    
+    static final String ctTextPlain          =  "text/plain" ;
+    static final String ctTextXML            =  "text/xml" ;
+    static final String ctTextStar           =  "text/*" ;
+    
+    static final String ctStarStar           = "*/*" ;
+    
+    @Test public void simpleNeg1()
+    { testMatch("text/plain", "text/plain", "text/plain") ; }
+    
+    @Test public void simpleNeg2()
+    { testMatch("application/xml", "text/plain", null) ; }
+    
+    @Test public void simpleNeg3()
+    { testMatch("text/*", "text/*", "text/*") ; }
+    
+    @Test public void simpleNeg4()
+    { testMatch("text/xml", "text/*", "text/xml") ; }
+    
+    @Test public void simpleNeg5()
+    { testMatch("text/*", "text/xml", "text/xml") ; }
+    
+    @Test public void listItemNeg1()
+    { testMatch("text/xml,text/*", "text/*", "text/xml") ; }
+    
+    @Test public void listListNeg1()
+    { testMatch("text/xml,text/*", "text/plain,text/*", "text/plain") ; }
+    
+    @Test public void listListNeg2()
+    { testMatch("text/xml,text/*", "text/*,text/plain", "text/xml") ; }
+    
+    @Test public void qualNeg1() { testMatch("text/xml;q=0.5,text/plain", "text/*", "text/plain") ; }
+    
+    @Test public void qualNeg2()
+    {
+        testMatch(
+                "application/n3,application/rdf+xml;q=0.5",
+                "application/rdf+xml,application/n3" , 
+                "application/n3") ;
+    }
+    
+    @Test public void qualNeg3()
+    {
+        testMatch(
+                "application/rdf+xml;q=0.5 , application/n3",
+                "application/n3,application/rdf+xml" , 
+                "application/n3") ;
+    }
+    
+    @Test public void qualNeg4()
+    {
+        testMatch(
+                "application/rdf+xml;q=0.5 , application/n3",
+                "application/rdf+xml , application/n3" , 
+                "application/n3") ;
+    }
+
+    // SPARQL: result set
+    @Test public void qualNeg5()
+    {
+        testMatch(
+                "application/sparql-results+json , application/sparql-results+xml;q=0.9 , application/rdf+xml , application/turtle;q=0.9 , */*;q=0.1",
+                "application/sparql-results+xml, application/sparql-results+json, text/csv , text/tab-separated-values, text/plain",
+                "application/sparql-results+json") ;
+    }
+    
+    // SPARQL: result set
+    @Test public void qualNeg5a()
+    {
+        testMatch(
+                "application/sparql-results+json , application/sparql-results+xml;q=0.9 , application/rdf+xml , application/turtle;q=0.9 , */*;q=0.1",
+                "application/sparql-results+json, application/sparql-results+xml, text/csv , text/tab-separated-values, text/plain",
+                "application/sparql-results+json") ;
+    }
+    
+    // SPARQL: RDF
+    @Test public void qualNeg6()
+    {
+        testMatch(
+                "application/sparql-results+json , application/sparql-results+xml;q=0.9 , application/rdf+xml , application/turtle;q=0.9 , */*;q=0.1",
+                "application/rdf+xml , application/turtle , application/x-turtle ,  text/turtle , text/plain  application/n-triples",
+                "application/rdf+xml") ;
+    }
+    
+    // HTTP: RDF
+    @Test public void qualNeg7()
+    {
+        testMatch(
+                "application/rdf+xml , application/turtle;q=0.9 , */*;q=0.1",
+                "application/rdf+xml , application/turtle , application/x-turtle ,  text/turtle , text/plain  application/n-triples",
+                "application/rdf+xml") ;
+    }
+    
+    // HTTP: RDF
+    @Test public void qualNeg8()
+    {
+        testMatch(
+                "application/turtle;q=0.9 , application/rdf+xml , */*;q=0.1",
+                "application/rdf+xml , application/turtle , application/x-turtle ,  text/turtle , text/plain  application/n-triples",
+                "application/rdf+xml") ;
+    }
+    
+    // TODO Standard headers from clients of RDf and for SPARQL results
+    
+    // RDF:
+    //  Accept: application/rdf+xml , application/turtle;q=0.9 , */*;q=0.1
+    //  Offer: application/rdf+xml , application/turtle , application/x-turtle ,  text/turtle , text/plain  application/n-triples
+    
+    // SPARQL:
+    //  Accept: application/sparql-results+json , application/sparql-results+xml;q=0.9 , application/rdf+xml , application/turtle;q=0.9 , */*;q=0.1
+    //  Offer:  application/sparql-results+xml, application/sparql-results+json, text/csv , text/tab-separated-values, text/plain
+    
+    private void testMatch(String header, String offer, String result)
+    {
+        AcceptList list1 = new AcceptList(header) ;
+        AcceptList list2 = new AcceptList(offer) ;
+        MediaType matchItem = AcceptList.match(list1, list2) ;
+
+        if ( result == null )
+        {
+            assertNull("Match not null: from "+q(header)+" :: "+q(offer),
+                       matchItem) ;
+            return ;
+        }
+        assertNotNull("Match is null: expected "+q(result), matchItem) ;
+        assertEquals("Match different", result, matchItem.toHeaderString()) ;
+    }
+    
+    private String q(Object obj)
+    {
+        if ( obj == null )
+            return "<null>" ;
+        return "'"+obj.toString()+"'" ;
+    }
+
+}