3333import org .eclipse .rdf4j .model .vocabulary .XSD ;
3434import org .eclipse .rdf4j .sail .SailException ;
3535import org .eclipse .rdf4j .sail .nativerdf .datastore .DataStore ;
36+ import org .eclipse .rdf4j .sail .nativerdf .model .CorruptIRI ;
37+ import org .eclipse .rdf4j .sail .nativerdf .model .CorruptIRIOrBNode ;
38+ import org .eclipse .rdf4j .sail .nativerdf .model .CorruptLiteral ;
3639import org .eclipse .rdf4j .sail .nativerdf .model .CorruptValue ;
3740import org .eclipse .rdf4j .sail .nativerdf .model .NativeBNode ;
3841import org .eclipse .rdf4j .sail .nativerdf .model .NativeIRI ;
3942import org .eclipse .rdf4j .sail .nativerdf .model .NativeLiteral ;
4043import org .eclipse .rdf4j .sail .nativerdf .model .NativeResource ;
4144import org .eclipse .rdf4j .sail .nativerdf .model .NativeValue ;
45+ import org .slf4j .Logger ;
46+ import org .slf4j .LoggerFactory ;
4247
4348/**
4449 * File-based indexed storage and retrieval of RDF values. ValueStore maps RDF values to integer IDs and vice-versa.
5055@ InternalUseOnly
5156public class ValueStore extends SimpleValueFactory {
5257
53- /*-----------*
54- * Constants *
55- *-----------*/
58+ private static final Logger logger = LoggerFactory .getLogger (ValueStore .class );
5659
5760 /**
5861 * The default value cache size.
@@ -127,7 +130,8 @@ public class ValueStore extends SimpleValueFactory {
127130 /**
128131 * Do not throw an exception in case a value cannot be loaded, e.g. due to a corrupt value store.
129132 */
130- private final boolean softFailOnCorruptData ;
133+ public static boolean SOFT_FAIL_ON_CORRUPT_DATA = "true"
134+ .equalsIgnoreCase (System .getProperty ("org.eclipse.rdf4j.sail.nativerdf.softFailOnCorruptData" ));;
131135
132136 /*--------------*
133137 * Constructors *
@@ -153,14 +157,6 @@ public ValueStore(File dataDir, boolean forceSync, int valueCacheSize, int value
153157
154158 setNewRevision ();
155159
156- /*
157- * Soft failure when a ValueStore is corrupt (i.e., one or more NativeValues cannot be read properly) can be
158- * enabled using the system property org.eclipse.rdf4j.sail.nativerdf.softFailOnCorruptData (boolean). The
159- * default behavior is that ValueStore will fail hard with a SailException, whereas softFaileOnCorruptData set
160- * to true will make ValueStore return instances of CorruptValue if NativeValue cannot be read.
161- */
162- this .softFailOnCorruptData = "true"
163- .equalsIgnoreCase (System .getProperty ("org.eclipse.rdf4j.sail.nativerdf.softFailOnCorruptData" ));
164160 }
165161
166162 /*---------*
@@ -195,6 +191,7 @@ public Lock getReadLock() throws InterruptedException {
195191 * @throws IOException If an I/O error occurred.
196192 */
197193 public NativeValue getValue (int id ) throws IOException {
194+
198195 // Check value cache
199196 Integer cacheID = id ;
200197 NativeValue resultValue = valueCache .get (cacheID );
@@ -206,12 +203,55 @@ public NativeValue getValue(int id) throws IOException {
206203 if (data != null ) {
207204 resultValue = data2value (id , data );
208205
209- // Store value in cache
210- valueCache .put (cacheID , resultValue );
206+ if (!(resultValue instanceof CorruptValue )) {
207+ // Store value in cache
208+ valueCache .put (cacheID , resultValue );
209+ }
211210 }
212211 }
213212
214213 return resultValue ;
214+
215+ }
216+
217+ /**
218+ * Gets the Resource for the specified ID.
219+ *
220+ * @param id A value ID.
221+ * @return The Resource for the ID, or <var>null</var> no such value could be found.
222+ * @throws IOException If an I/O error occurred.
223+ */
224+ public <T extends NativeValue & Resource > T getResource (int id ) throws IOException {
225+
226+ NativeValue resultValue = getValue (id );
227+
228+ if (!(resultValue instanceof Resource )) {
229+ if (SOFT_FAIL_ON_CORRUPT_DATA && resultValue instanceof CorruptValue ) {
230+ return (T ) new CorruptIRIOrBNode (revision , id , ((CorruptValue ) resultValue ).getData ());
231+ }
232+ }
233+
234+ return (T ) resultValue ;
235+ }
236+
237+ /**
238+ * Gets the IRI for the specified ID.
239+ *
240+ * @param id A value ID.
241+ * @return The IRI for the ID, or <var>null</var> no such value could be found.
242+ * @throws IOException If an I/O error occurred.
243+ */
244+ public <T extends NativeValue & IRI > T getIRI (int id ) throws IOException {
245+
246+ NativeValue resultValue = getValue (id );
247+
248+ if (!(resultValue instanceof Resource )) {
249+ if (SOFT_FAIL_ON_CORRUPT_DATA && resultValue instanceof CorruptValue ) {
250+ return (T ) new CorruptIRIOrBNode (revision , id , ((CorruptValue ) resultValue ).getData ());
251+ }
252+ }
253+
254+ return (T ) resultValue ;
215255 }
216256
217257 /**
@@ -542,7 +582,8 @@ private boolean isNamespaceData(byte[] data) {
542582
543583 private NativeValue data2value (int id , byte [] data ) throws IOException {
544584 if (data .length == 0 ) {
545- if (softFailOnCorruptData ) {
585+ if (SOFT_FAIL_ON_CORRUPT_DATA ) {
586+ logger .error ("Soft fail on corrupt data: Empty data array for value with id {}" , id );
546587 return new CorruptValue (revision , id , data );
547588 }
548589 throw new SailException ("Empty data array for value with id " + id );
@@ -555,52 +596,69 @@ private NativeValue data2value(int id, byte[] data) throws IOException {
555596 case LITERAL_VALUE :
556597 return data2literal (id , data );
557598 default :
558- if (softFailOnCorruptData ) {
599+ if (SOFT_FAIL_ON_CORRUPT_DATA ) {
600+ logger .error ("Soft fail on corrupt data: Invalid type {} for value with id {}" , data [0 ], id );
559601 return new CorruptValue (revision , id , data );
560602 }
561603 throw new SailException ("Invalid type " + data [0 ] + " for value with id " + id );
562604 }
563605 }
564606
565- private NativeIRI data2uri (int id , byte [] data ) throws IOException {
566- int nsID = ByteArrayUtil .getInt (data , 1 );
567- String namespace = getNamespace (nsID );
607+ private <T extends IRI & NativeValue > T data2uri (int id , byte [] data ) throws IOException {
608+ try {
609+ int nsID = ByteArrayUtil .getInt (data , 1 );
610+ String namespace = getNamespace (nsID );
611+
612+ String localName = new String (data , 5 , data .length - 5 , StandardCharsets .UTF_8 );
568613
569- String localName = new String (data , 5 , data .length - 5 , StandardCharsets .UTF_8 );
614+ return (T ) new NativeIRI (revision , namespace , localName , id );
615+ } catch (Throwable e ) {
616+ if (SOFT_FAIL_ON_CORRUPT_DATA && (e instanceof Exception || e instanceof AssertionError )) {
617+ return (T ) new CorruptIRI (revision , id , data );
618+ }
619+ throw e ;
620+ }
570621
571- return new NativeIRI (revision , namespace , localName , id );
572622 }
573623
574624 private NativeBNode data2bnode (int id , byte [] data ) {
575625 String nodeID = new String (data , 1 , data .length - 1 , StandardCharsets .UTF_8 );
576626 return new NativeBNode (revision , nodeID , id );
577627 }
578628
579- private NativeLiteral data2literal (int id , byte [] data ) throws IOException {
580- // Get datatype
581- int datatypeID = ByteArrayUtil .getInt (data , 1 );
582- IRI datatype = null ;
583- if (datatypeID != NativeValue .UNKNOWN_ID ) {
584- datatype = (IRI ) getValue (datatypeID );
585- }
629+ private <T extends NativeValue & Literal > T data2literal (int id , byte [] data ) throws IOException {
630+ try {
631+ // Get datatype
632+ int datatypeID = ByteArrayUtil .getInt (data , 1 );
633+ IRI datatype = null ;
634+ if (datatypeID != NativeValue .UNKNOWN_ID ) {
635+ datatype = (IRI ) getValue (datatypeID );
636+ }
586637
587- // Get language tag
588- String lang = null ;
589- int langLength = data [5 ];
590- if (langLength > 0 ) {
591- lang = new String (data , 6 , langLength , StandardCharsets .UTF_8 );
592- }
638+ // Get language tag
639+ String lang = null ;
640+ int langLength = data [5 ];
641+ if (langLength > 0 ) {
642+ lang = new String (data , 6 , langLength , StandardCharsets .UTF_8 );
643+ }
593644
594- // Get label
595- String label = new String (data , 6 + langLength , data .length - 6 - langLength , StandardCharsets .UTF_8 );
645+ // Get label
646+ String label = new String (data , 6 + langLength , data .length - 6 - langLength , StandardCharsets .UTF_8 );
596647
597- if (lang != null ) {
598- return new NativeLiteral (revision , label , lang , id );
599- } else if (datatype != null ) {
600- return new NativeLiteral (revision , label , datatype , id );
601- } else {
602- return new NativeLiteral (revision , label , CoreDatatype .XSD .STRING , id );
648+ if (lang != null ) {
649+ return (T ) new NativeLiteral (revision , label , lang , id );
650+ } else if (datatype != null ) {
651+ return (T ) new NativeLiteral (revision , label , datatype , id );
652+ } else {
653+ return (T ) new NativeLiteral (revision , label , CoreDatatype .XSD .STRING , id );
654+ }
655+ } catch (Throwable e ) {
656+ if (SOFT_FAIL_ON_CORRUPT_DATA && (e instanceof Exception || e instanceof AssertionError )) {
657+ return (T ) new CorruptLiteral (revision , id , data );
658+ }
659+ throw e ;
603660 }
661+
604662 }
605663
606664 private String data2namespace (byte [] data ) {
0 commit comments