PageRenderTime 51ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/core/src/test/java/com/github/jsonldjava/impl/TurtleRDFParserTest.java

http://github.com/tristan/jsonld-java
Java | 448 lines | 362 code | 31 blank | 55 comment | 77 complexity | 8e784411dbe357b12aade171e4fd46cf MD5 | raw file
Possible License(s): BSD-3-Clause
  1. package com.github.jsonldjava.impl;
  2. import static org.junit.Assert.assertNotNull;
  3. import static org.junit.Assert.assertTrue;
  4. import java.io.IOException;
  5. import java.net.URISyntaxException;
  6. import java.util.ArrayList;
  7. import java.util.Collection;
  8. import java.util.LinkedHashMap;
  9. import java.util.List;
  10. import java.util.Map;
  11. import java.util.Map.Entry;
  12. import org.junit.BeforeClass;
  13. import org.junit.Ignore;
  14. import org.junit.Test;
  15. import org.junit.runner.RunWith;
  16. import org.junit.runners.Parameterized;
  17. import org.junit.runners.Parameterized.Parameters;
  18. import com.github.jsonldjava.core.JSONLD;
  19. import com.github.jsonldjava.core.JSONLDProcessingError;
  20. import com.github.jsonldjava.core.Options;
  21. import com.github.jsonldjava.core.RDFDataset;
  22. import com.github.jsonldjava.core.RDFDataset.Quad;
  23. import com.github.jsonldjava.core.RDFDatasetUtils;
  24. import com.github.jsonldjava.utils.EarlTestSuite;
  25. import com.github.jsonldjava.utils.JSONUtils;
  26. import com.github.jsonldjava.utils.Obj;
  27. @Ignore
  28. @RunWith(Parameterized.class)
  29. public class TurtleRDFParserTest {
  30. // @Test
  31. public void simpleTest() throws JSONLDProcessingError {
  32. final String input = "@prefix ericFoaf: <http://www.w3.org/People/Eric/ericP-foaf.rdf#> .\n"
  33. + "@prefix : <http://xmlns.com/foaf/0.1/> .\n"
  34. + "ericFoaf:ericP :givenName \"Eric\" ;\n"
  35. + "\t:knows <http://norman.walsh.name/knows/who/dan-brickley> ,\n"
  36. + "\t\t[ :mbox <mailto:timbl@w3.org> ] ,\n" + "\t\t<http://getopenid.com/amyvdh> .";
  37. final List<Map<String, Object>> expected = new ArrayList<Map<String, Object>>() {
  38. {
  39. add(new LinkedHashMap<String, Object>() {
  40. {
  41. put("@id", "_:b1");
  42. put("http://xmlns.com/foaf/0.1/mbox", new ArrayList<Object>() {
  43. {
  44. add(new LinkedHashMap<String, Object>() {
  45. {
  46. put("@id", "mailto:timbl@w3.org");
  47. }
  48. });
  49. }
  50. });
  51. }
  52. });
  53. add(new LinkedHashMap<String, Object>() {
  54. {
  55. put("@id", "http://getopenid.com/amyvdh");
  56. }
  57. });
  58. add(new LinkedHashMap<String, Object>() {
  59. {
  60. put("@id", "http://norman.walsh.name/knows/who/dan-brickley");
  61. }
  62. });
  63. add(new LinkedHashMap<String, Object>() {
  64. {
  65. put("@id", "http://www.w3.org/People/Eric/ericP-foaf.rdf#ericP");
  66. put("http://xmlns.com/foaf/0.1/givenName", new ArrayList<Object>() {
  67. {
  68. add(new LinkedHashMap<String, Object>() {
  69. {
  70. put("@value", "Eric");
  71. }
  72. });
  73. }
  74. });
  75. put("http://xmlns.com/foaf/0.1/knows", new ArrayList<Object>() {
  76. {
  77. add(new LinkedHashMap<String, Object>() {
  78. {
  79. put("@id",
  80. "http://norman.walsh.name/knows/who/dan-brickley");
  81. }
  82. });
  83. add(new LinkedHashMap<String, Object>() {
  84. {
  85. put("@id", "_:b1");
  86. }
  87. });
  88. add(new LinkedHashMap<String, Object>() {
  89. {
  90. put("@id", "http://getopenid.com/amyvdh");
  91. }
  92. });
  93. }
  94. });
  95. }
  96. });
  97. add(new LinkedHashMap<String, Object>() {
  98. {
  99. put("@id", "mailto:timbl@w3.org");
  100. }
  101. });
  102. }
  103. };
  104. final Object json = JSONLD.fromRDF(input, new Options() {
  105. {
  106. format = "text/turtle";
  107. }
  108. }, new TurtleRDFParser());
  109. assertTrue(JSONUtils.equals(expected, json));
  110. }
  111. @BeforeClass
  112. public static void before() {
  113. if (CACHE_DIR == null) {
  114. System.out.println("Using temp dir: " + System.getProperty("java.io.tmpdir"));
  115. }
  116. }
  117. private static String TURTLE_TEST_MANIFEST = "https://dvcs.w3.org/hg/rdf/raw-file/default/rdf-turtle/tests-ttl/manifest.ttl";
  118. private static final String LAST_ETAG = null; // "1369157887.0";
  119. private static final String CACHE_DIR = null;
  120. @Parameters(name = "{0}{1}")
  121. public static Collection<Object[]> data() throws URISyntaxException, IOException {
  122. final EarlTestSuite testSuite = new EarlTestSuite(TURTLE_TEST_MANIFEST, CACHE_DIR,
  123. LAST_ETAG);
  124. final Collection<Object[]> rdata = new ArrayList<Object[]>();
  125. for (final Map<String, Object> test : testSuite.getTests()) {
  126. rdata.add(new Object[] { testSuite, test.get("@id"), test });
  127. }
  128. return rdata;
  129. }
  130. private final Map<String, Object> test;
  131. private final EarlTestSuite testSuite;
  132. public TurtleRDFParserTest(final EarlTestSuite testSuite, final String id,
  133. final Map<String, Object> test) {
  134. this.test = test;
  135. this.testSuite = testSuite;
  136. }
  137. @Test
  138. public void runTest() throws IOException, JSONLDProcessingError {
  139. final String inputfn = (String) Obj.get(test, "mf:action", "@id");
  140. final String outputfn = (String) Obj.get(test, "mf:result", "@id");
  141. final String type = (String) Obj.get(test, "@type");
  142. final String input = testSuite.getFile(inputfn);
  143. Boolean passed = false;
  144. String failmsg = "";
  145. if ("rdft:TestTurtleEval".equals(type)) {
  146. final RDFDataset result = new TurtleRDFParser().parse(input);
  147. final RDFDataset expected = RDFDatasetUtils.parseNQuads(testSuite.getFile(outputfn));
  148. passed = compareDatasets("http://example/base/" + inputfn, result, expected);
  149. if (!passed) {
  150. failmsg = "\n" + "Expected: " + RDFDatasetUtils.toNQuads(expected) + "\n"
  151. + "Result : " + RDFDatasetUtils.toNQuads(result);
  152. }
  153. } else if ("rdft:TestTurtlePositiveSyntax".equals(type)) {
  154. JSONLD.fromRDF(input, new Options("http://example/base/") {
  155. {
  156. format = "text/turtle";
  157. }
  158. });
  159. passed = true; // otherwise an exception would have been thrown
  160. } else if ("rdft:TestTurtleNegativeSyntax".equals(type)
  161. || "rdft:TestTurtleNegativeEval".equals(type)) {
  162. // TODO: need to figure out how to properly deal with negative tests
  163. try {
  164. JSONLD.fromRDF(input, new Options("http://example/base/") {
  165. {
  166. format = "text/turtle";
  167. }
  168. });
  169. failmsg = "Expected parse error, but no problems detected";
  170. } catch (final JSONLDProcessingError e) {
  171. if (e.getType() == JSONLDProcessingError.Error.PARSE_ERROR) {
  172. passed = true;
  173. } else {
  174. failmsg = "Expected parse error, got: " + e.getMessage();
  175. }
  176. }
  177. } else {
  178. failmsg = "DON'T KNOW HOW TO HANDLE: " + type;
  179. }
  180. assertTrue(failmsg, passed);
  181. }
  182. /**
  183. * Compare datasets, normalizing the blank nodes and adding baseIRI to
  184. * relative IRIs
  185. *
  186. * @param result
  187. * @param expected
  188. * @return
  189. */
  190. private Boolean compareDatasets(final String baseIRI, final RDFDataset result,
  191. final RDFDataset expected) {
  192. final String baseIRIpath = baseIRI.substring(0, baseIRI.lastIndexOf("/") + 1);
  193. final List<RDFDataset.Quad> res = new ArrayList<RDFDataset.Quad>() {
  194. {
  195. for (final RDFDataset.Quad q : result.getQuads("@default")) {
  196. final RDFDataset.Node s = q.getSubject();
  197. final RDFDataset.Node p = q.getPredicate();
  198. final RDFDataset.Node o = q.getObject();
  199. if (s.isIRI() && !s.getValue().contains(":")) {
  200. final String v = s.getValue();
  201. if (v.startsWith("#") || v.startsWith("?")) {
  202. s.put("value", baseIRI + s.getValue());
  203. } else {
  204. s.put("value", baseIRIpath + s.getValue());
  205. }
  206. }
  207. if (p.isIRI() && !p.getValue().contains(":")) {
  208. final String v = p.getValue();
  209. if (v.startsWith("#") || v.startsWith("?")) {
  210. p.put("value", baseIRI + p.getValue());
  211. } else {
  212. p.put("value", baseIRIpath + p.getValue());
  213. }
  214. }
  215. if (o.isIRI() && !o.getValue().contains(":")) {
  216. final String v = o.getValue();
  217. if (v.startsWith("#") || v.startsWith("?")) {
  218. o.put("value", baseIRI + o.getValue());
  219. } else {
  220. o.put("value", baseIRIpath + o.getValue());
  221. }
  222. }
  223. add(q);
  224. }
  225. }
  226. };
  227. final List<RDFDataset.Quad> exp = new ArrayList<RDFDataset.Quad>() {
  228. {
  229. addAll(expected.getQuads("@default"));
  230. }
  231. };
  232. final List<RDFDataset.Quad> unmatched = new ArrayList<RDFDataset.Quad>();
  233. final BnodeMappings bnodeMaps = new BnodeMappings();
  234. boolean finalpass = false;
  235. while (!exp.isEmpty() && !res.isEmpty()) {
  236. final Quad eq = exp.remove(0);
  237. int matches = 0;
  238. RDFDataset.Quad last_match = null;
  239. for (final RDFDataset.Quad rq : res) {
  240. // if predicates are not equal there cannot be a match
  241. if (!eq.getPredicate().equals(rq.getPredicate())) {
  242. continue;
  243. }
  244. if (eq.getSubject().isBlankNode() && rq.getSubject().isBlankNode()) {
  245. // check for locking
  246. boolean subjectLocked = false;
  247. if (bnodeMaps.isLocked(eq.getSubject().getValue())) {
  248. // if this mapping doesn't match the locked mapping, we
  249. // don't have a match
  250. if (!rq.getSubject().getValue()
  251. .equals(bnodeMaps.getMapping(eq.getSubject().getValue()))) {
  252. continue;
  253. }
  254. subjectLocked = true;
  255. }
  256. // if the objects are also both blank nodes
  257. if (eq.getObject().isBlankNode() && rq.getObject().isBlankNode()) {
  258. // check for locking
  259. if (bnodeMaps.isLocked(eq.getObject().getValue())) {
  260. // if this mapping doesn't match the locked mapping,
  261. // we don't have a match
  262. if (!rq.getObject().getValue()
  263. .equals(bnodeMaps.getMapping(eq.getObject().getValue()))) {
  264. continue;
  265. }
  266. } else {
  267. // add possible mappings for the objects
  268. bnodeMaps.addPossibleMapping(eq.getObject().getValue(), rq.getObject()
  269. .getValue());
  270. }
  271. }
  272. // otherwise, if the objects aren't equal we can't have a
  273. // match
  274. else if (!eq.getObject().equals(rq.getObject())) {
  275. continue;
  276. }
  277. // objects are equal or both blank nodes so we have a match
  278. matches++;
  279. last_match = rq;
  280. // if subject is not locked add a possible mapping between
  281. // subjects
  282. if (!subjectLocked) {
  283. bnodeMaps.addPossibleMapping(eq.getSubject().getValue(), rq.getSubject()
  284. .getValue());
  285. }
  286. }
  287. // otherwise check if the subjects are equal
  288. else if (eq.getSubject().equals(rq.getSubject())) {
  289. // if both objects are blank nodes, add possible mappings
  290. // for them
  291. if (eq.getObject().isBlankNode() && rq.getObject().isBlankNode()) {
  292. // check for locking
  293. if (bnodeMaps.isLocked(eq.getObject().getValue())) {
  294. // if this mapping doesn't match the locked mapping,
  295. // we don't have a match
  296. if (!rq.getObject().getValue()
  297. .equals(bnodeMaps.getMapping(eq.getObject().getValue()))) {
  298. continue;
  299. }
  300. } else {
  301. // add possible mappings for the objects
  302. bnodeMaps.addPossibleMapping(eq.getObject().getValue(), rq.getObject()
  303. .getValue());
  304. }
  305. // if we get here we have a match
  306. matches++;
  307. last_match = rq;
  308. }
  309. // otherwise, if the objects are equal we we have an exact
  310. // match
  311. else if (eq.getObject().equals(rq.getObject())) {
  312. matches = 1;
  313. last_match = rq;
  314. break;
  315. }
  316. }
  317. }
  318. if (matches == 0) {
  319. // if we didn't find any matches, we're done and things didn't
  320. // match!
  321. return false;
  322. } else if (matches == 1) {
  323. // we have one match
  324. if (eq.getSubject().isBlankNode()) {
  325. // lock this mapping
  326. bnodeMaps.lockMapping(eq.getSubject().getValue(), last_match.getSubject()
  327. .getValue());
  328. }
  329. if (eq.getObject().isBlankNode()) {
  330. // lock this mapping
  331. bnodeMaps.lockMapping(eq.getObject().getValue(), last_match.getObject()
  332. .getValue());
  333. }
  334. res.remove(last_match);
  335. } else {
  336. // we got multiple matches, we need to figure this stuff out
  337. // later!
  338. unmatched.add(eq);
  339. }
  340. // TODO: no tests so far test this out, make one!
  341. if (exp.isEmpty() && !finalpass) {
  342. // if we are at the end and we have unmatched triples
  343. if (!unmatched.isEmpty()) {
  344. // lock the remaining bnodes, and test again
  345. bnodeMaps.lockRemaining();
  346. exp.addAll(unmatched);
  347. unmatched.clear();
  348. }
  349. // we also only want to do this once, if we get here again
  350. // without matching everything
  351. // we're not going to match everything
  352. finalpass = true;
  353. }
  354. }
  355. // they both matched if we have nothing left over
  356. return res.isEmpty() && exp.isEmpty() && unmatched.isEmpty();
  357. }
  358. private class BnodeMappings {
  359. Map<String, Map<String, Integer>> possiblebnodemappings = new LinkedHashMap<String, Map<String, Integer>>();
  360. Map<String, String> lockedbnodemappings = new LinkedHashMap<String, String>();
  361. public void lockMapping(final String bn1, final String bn2) {
  362. lockedbnodemappings.put(bn1, bn2);
  363. possiblebnodemappings.remove(bn1);
  364. for (final String i : possiblebnodemappings.keySet()) {
  365. // remove bn2 as a possible mapping for any other bnodes
  366. possiblebnodemappings.get(i).remove(bn2);
  367. }
  368. }
  369. public void lockRemaining() {
  370. final List<String> unlocked = new ArrayList<String>(possiblebnodemappings.keySet());
  371. for (final String bn1 : unlocked) {
  372. final String bn2 = getMapping(bn1);
  373. assertNotNull("Unable to find mapping for blank node " + bn1
  374. + ". Possible error in mapping code", bn2);
  375. lockMapping(bn1, bn2);
  376. }
  377. }
  378. public boolean isLocked(final String b) {
  379. return lockedbnodemappings.containsKey(b);
  380. }
  381. /**
  382. * return either the locked mapping, or the highest matching
  383. *
  384. * @param b
  385. * @return
  386. */
  387. public String getMapping(final String b) {
  388. if (isLocked(b)) {
  389. return lockedbnodemappings.get(b);
  390. } else {
  391. int max = -1;
  392. String rval = null;
  393. for (final Entry<String, Integer> map : possiblebnodemappings.get(b).entrySet()) {
  394. if (map.getValue() > max) {
  395. max = map.getValue();
  396. rval = map.getKey();
  397. }
  398. }
  399. return rval;
  400. }
  401. }
  402. public void addPossibleMapping(final String bn1, final String bn2) {
  403. Map<String, Integer> bn1m;
  404. if (possiblebnodemappings.containsKey(bn1)) {
  405. bn1m = possiblebnodemappings.get(bn1);
  406. } else {
  407. bn1m = new LinkedHashMap<String, Integer>();
  408. possiblebnodemappings.put(bn1, bn1m);
  409. }
  410. Integer mappingcount = 0;
  411. if (bn1m.containsKey(bn2)) {
  412. mappingcount = bn1m.get(bn2);
  413. }
  414. bn1m.put(bn2, mappingcount + 1);
  415. }
  416. }
  417. }