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()+"'" ;
+ }
+
+}