001/** 002 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 003 * This file is part of the LDP4j Project: 004 * http://www.ldp4j.org/ 005 * 006 * Center for Open Middleware 007 * http://www.centeropenmiddleware.com/ 008 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 009 * Copyright (C) 2014-2016 Center for Open Middleware. 010 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 011 * Licensed under the Apache License, Version 2.0 (the "License"); 012 * you may not use this file except in compliance with the License. 013 * You may obtain a copy of the License at 014 * 015 * http://www.apache.org/licenses/LICENSE-2.0 016 * 017 * Unless required by applicable law or agreed to in writing, software 018 * distributed under the License is distributed on an "AS IS" BASIS, 019 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 020 * See the License for the specific language governing permissions and 021 * limitations under the License. 022 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 023 * Artifact : org.ldp4j.framework:ldp4j-application-api:0.2.1 024 * Bundle : ldp4j-application-api-0.2.1.jar 025 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 026 */ 027package org.ldp4j.application.data.constraints; 028 029import static com.google.common.base.Preconditions.checkArgument; 030import static com.google.common.base.Preconditions.checkNotNull; 031 032import java.io.Serializable; 033import java.net.URI; 034import java.util.Collection; 035import java.util.List; 036import java.util.Map; 037import java.util.Set; 038 039import org.ldp4j.application.data.DataSet; 040import org.ldp4j.application.data.Individual; 041import org.ldp4j.application.data.Literal; 042import org.ldp4j.application.data.Value; 043import org.ldp4j.application.data.ValueVisitor; 044 045import com.google.common.base.MoreObjects; 046import com.google.common.collect.ImmutableList; 047import com.google.common.collect.ImmutableList.Builder; 048import com.google.common.collect.ImmutableSet; 049import com.google.common.collect.Lists; 050import com.google.common.collect.Maps; 051import com.google.common.collect.Sets; 052 053public final class Constraints implements Serializable { 054 055 public enum NodeKind { 056 NODE("Node"), 057 BLANK_NODE_OR_IRI("BlankNodeOrIRI"), 058 BLANK_NODE_OR_LITERAL("BlankNodeOrLiteral"), 059 LITERAL_OR_IRI("LiteralOrIRI"), 060 BLANK_NODE("BlankNode"), 061 IRI("IRI"), 062 LITERAL("Literal") 063 ; 064 065 private final String localName; 066 067 private NodeKind(String localName) { 068 this.localName = localName; 069 } 070 071 public String localName() { 072 return localName; 073 } 074 } 075 076 public interface Describable { 077 078 String label(); 079 String comment(); 080 } 081 082 public static final class Cardinality implements Serializable { 083 084 private static final long serialVersionUID = 7262473645776142538L; 085 086 private int min; 087 private int max; 088 089 private Cardinality(int min, int max) { 090 this.min = min; 091 this.max = max; 092 } 093 094 public int min() { 095 return this.min; 096 } 097 098 public int max() { 099 return this.max; 100 } 101 102 /** 103 * {@inheritDoc} 104 */ 105 @Override 106 public String toString() { 107 return 108 MoreObjects. 109 toStringHelper(getClass()). 110 add("min", this.min). 111 add("max", this.max). 112 toString(); 113 } 114 115 public static Cardinality mandatory() { 116 return new Cardinality(1,1); 117 } 118 119 public static Cardinality atMost(int max) { 120 return new Cardinality(0,max); 121 } 122 123 public static Cardinality atLeast(int min) { 124 return new Cardinality(min,-1); 125 } 126 127 public static Cardinality create(int min, int max) { 128 return new Cardinality(min,max); 129 } 130 131 public static Cardinality optional() { 132 return new Cardinality(0,1); 133 } 134 135 public static Cardinality unbound() { 136 return new Cardinality(0,-1); 137 } 138 139 } 140 141 142 public abstract static class AbstractPropertyConstraint<T extends AbstractPropertyConstraint<T>> implements Serializable { 143 144 private static final long serialVersionUID = 6473281395518369031L; 145 146 private static final class ValueCollector implements ValueVisitor { 147 148 private final Collection<Serializable> individuals; 149 private final Collection<Literal<?>> literals; 150 151 private ValueCollector(Collection<Serializable> individuals, Collection<Literal<?>> lLiterals) { 152 this.individuals = individuals; 153 this.literals = lLiterals; 154 } 155 156 @Override 157 public void visitLiteral(Literal<?> value) { 158 literals.add(value); 159 } 160 161 @Override 162 public void visitIndividual(Individual<?, ?> value) { 163 individuals.add(value.id()); 164 } 165 166 } 167 168 private final URI predicate; 169 private String comment; 170 private String label; 171 private Set<Serializable> allowedIndividuals; 172 private Set<Literal<?>> allowedLiterals; 173 private List<Serializable> individuals; 174 private List<Literal<?>> literals; 175 private URI datatype; 176 private NodeKind nodeKind; 177 private Shape valueShape; 178 private URI valueType; 179 private Cardinality cardinality; 180 181 protected AbstractPropertyConstraint(URI predicate) { 182 this.predicate=predicate; 183 } 184 185 protected abstract T delegate(); 186 187 public T withLabel(String label) { 188 checkNotNull(label,"Label cannot be null"); 189 this.label=label; 190 return delegate(); 191 } 192 193 public String label() { 194 return this.label; 195 } 196 197 public T withComment(String comment) { 198 checkNotNull(comment,"Comment cannot be null"); 199 this.comment=comment; 200 return delegate(); 201 } 202 203 public String comment() { 204 return this.comment; 205 } 206 207 public URI predicate() { 208 return this.predicate; 209 } 210 211 public T withCardinality(Cardinality cardinality) { 212 checkNotNull(cardinality,"Cardinality cannot be null"); 213 this.cardinality=cardinality; 214 return delegate(); 215 } 216 217 public Cardinality cardinality() { 218 Cardinality result = this.cardinality; 219 if(result==null) { 220 result=Cardinality.unbound(); 221 } 222 return result; 223 } 224 225 public T withAllowedValues(Value... allowedValues) { 226 checkNotNull(allowedValues,"Allowed values cannot be null"); 227 this.allowedLiterals=Sets.newLinkedHashSet(); 228 this.allowedIndividuals=Sets.newLinkedHashSet(); 229 ValueCollector valueCollector=new ValueCollector(this.allowedIndividuals,this.allowedLiterals); 230 for(Value value:allowedValues) { 231 value.accept(valueCollector); 232 } 233 return delegate(); 234 } 235 236 public Set<Literal<?>> allowedLiterals() { 237 Set<Literal<?>> result=this.allowedLiterals; 238 if(result==null) { 239 result=Sets.newLinkedHashSet(); 240 } 241 return ImmutableSet.copyOf(result); 242 } 243 244 public Set<Individual<?,?>> allowedIndividuals(DataSet dataSet) { 245 Set<Serializable> result=this.allowedIndividuals; 246 if(result==null) { 247 result=Sets.newLinkedHashSet(); 248 } 249 return ConstraintsHelper.getOrCreateIndividuals(dataSet,result); 250 } 251 252 public T withDatatype(URI datatype) { 253 checkNotNull(datatype,"Datatype cannot be null"); 254 this.datatype = datatype; 255 return delegate(); 256 } 257 258 public URI datatype() { 259 return this.datatype; 260 } 261 262 public T withValue(Value... values) { 263 checkNotNull(values,"Value cannot be null"); 264 this.literals=Lists.newArrayList(); 265 this.individuals=Lists.newArrayList(); 266 ValueCollector valueCollector=new ValueCollector(this.individuals,this.literals); 267 for(Value value:values) { 268 value.accept(valueCollector); 269 } 270 return delegate(); 271 } 272 273 public List<Literal<?>> literals() { 274 List<Literal<?>> result=this.literals; 275 if(result==null) { 276 result=Lists.newArrayList(); 277 } 278 return ImmutableList.copyOf(result); 279 } 280 281 public List<Individual<?,?>> individuals(DataSet dataSet) { 282 List<Serializable> result=this.individuals; 283 if(result==null) { 284 result=Lists.newArrayList(); 285 } 286 return ConstraintsHelper.getOrCreateIndividuals(dataSet,result); 287 } 288 289 public T withNodeKind(NodeKind nodeKind) { 290 checkNotNull(nodeKind,"Node kind cannot be null"); 291 this.nodeKind=nodeKind; 292 return delegate(); 293 } 294 295 public NodeKind nodeKind() { 296 return this.nodeKind; 297 } 298 299 public T withValueShape(Shape valueShape) { 300 this.valueShape = valueShape; 301 return delegate(); 302 } 303 304 public Shape valueShape() { 305 return this.valueShape; 306 } 307 308 public T withValueType(URI valueType) { 309 this.valueType = valueType; 310 return delegate(); 311 } 312 313 public URI valueType() { 314 return this.valueType; 315 } 316 317 @Override 318 public String toString() { 319 return 320 MoreObjects. 321 toStringHelper(getClass()). 322 omitNullValues(). 323 add("predicate", this.predicate). 324 add("label", this.label). 325 add("comment", this.comment). 326 add("cardinality", this.cardinality()). 327 add("allowedLiterals", this.allowedLiterals). 328 add("allowedIndividuals", this.allowedIndividuals). 329 add("datatype", this.datatype). 330 add("literals", this.literals). 331 add("literals", this.individuals). 332 add("nodeKind", this.nodeKind). 333 add("valueShape", this.valueShape). 334 add("valueType", this.valueType). 335 toString(); 336 } 337 338 } 339 340 public static final class PropertyConstraint extends AbstractPropertyConstraint<PropertyConstraint> implements Describable { 341 342 private static final long serialVersionUID = -2646499801130951583L; 343 344 private PropertyConstraint(URI predicate) { 345 super(predicate); 346 } 347 348 @Override 349 protected PropertyConstraint delegate() { 350 return this; 351 } 352 353 } 354 355 public static final class InversePropertyConstraint extends AbstractPropertyConstraint<InversePropertyConstraint> implements Describable { 356 357 private static final long serialVersionUID = -6328974380403084873L; 358 359 private InversePropertyConstraint(URI predicate) { 360 super(predicate); 361 } 362 363 @Override 364 protected InversePropertyConstraint delegate() { 365 return this; 366 } 367 368 } 369 370 public static final class Shape implements Describable, Serializable { 371 372 private static final long serialVersionUID = 3966457418001884744L; 373 374 private Map<URI,AbstractPropertyConstraint<?>> constraints; // NOSONAR 375 private String label; 376 private String comment; 377 378 private Shape() { 379 this.constraints=Maps.newLinkedHashMap(); 380 } 381 382 public Shape withLabel(String label) { 383 this.label=label; 384 return this; 385 } 386 387 /** 388 * {@inheritDoc} 389 */ 390 @Override 391 public String label() { 392 return this.label; 393 } 394 395 public Shape withComment(String comment) { 396 this.comment=comment; 397 return this; 398 } 399 400 /** 401 * {@inheritDoc} 402 */ 403 @Override 404 public String comment() { 405 return this.comment; 406 } 407 408 public Shape withPropertyConstraint(PropertyConstraint constraint) { 409 checkNotNull(constraint); 410 URI predicate = constraint.predicate(); 411 checkArgument(!this.constraints.containsKey(predicate),"Shape already defines constraints for predicate '"+predicate+"'"); 412 this.constraints.put(predicate,constraint); 413 return this; 414 } 415 416 public Shape withPropertyConstraint(InversePropertyConstraint constraint) { 417 checkNotNull(constraint); 418 URI predicate = constraint.predicate(); 419 checkArgument(!this.constraints.containsKey(predicate),"Shape already defines constraints for predicate '"+predicate+"'"); 420 this.constraints.put(predicate,constraint); 421 return this; 422 } 423 424 public List<PropertyConstraint> propertyConstraints() { 425 final Builder<PropertyConstraint> builder=ImmutableList.<PropertyConstraint>builder(); 426 filter(builder,PropertyConstraint.class); 427 return builder.build(); 428 } 429 430 public List<InversePropertyConstraint> inversePropertyConstraints() { 431 final Builder<InversePropertyConstraint> builder=ImmutableList.<InversePropertyConstraint>builder(); 432 filter(builder,InversePropertyConstraint.class); 433 return builder.build(); 434 } 435 436 private <T extends AbstractPropertyConstraint<?>> void filter(Builder<T> builder, Class<? extends T> clazz) { 437 for(AbstractPropertyConstraint<?> c:this.constraints.values()) { 438 if(clazz.isInstance(c)) { 439 builder.add(clazz.cast(c)); 440 } 441 } 442 } 443 444 @Override 445 public String toString() { 446 return 447 MoreObjects. 448 toStringHelper(getClass()). 449 omitNullValues(). 450 add("label", this.label). 451 add("comment", this.comment). 452 add("constraints",this.constraints). 453 toString(); 454 } 455 456 } 457 458 private static final long serialVersionUID = 4368698694568719975L; 459 460 private Map<Serializable,Shape> nodeShapes; // NOSONAR 461 private Map<URI,Shape> typeShapes; // NOSONAR 462 463 private Constraints() { 464 this.nodeShapes=Maps.newLinkedHashMap(); 465 this.typeShapes=Maps.newLinkedHashMap(); 466 } 467 468 public List<Shape> shapes() { 469 return 470 ImmutableList. 471 <Shape>builder(). 472 addAll(this.typeShapes.values()). 473 addAll(this.nodeShapes.values()). 474 build(); 475 } 476 477 public Set<URI> types() { 478 return ImmutableSet.copyOf(this.typeShapes.keySet()); 479 } 480 481 public Set<Individual<?,?>> nodes(DataSet dataSet) { 482 checkNotNull(dataSet,"Data set cannot be null"); 483 return 484 ConstraintsHelper. 485 getOrCreateIndividuals( 486 dataSet, 487 this.nodeShapes.keySet()); 488 } 489 490 public Shape typeShape(URI type) { 491 return this.typeShapes.get(type); 492 } 493 494 public Shape nodeShape(Individual<?,?> individual) { 495 return this.nodeShapes.get(individual.id()); 496 } 497 498 public Constraints withTypeShape(URI type, Shape shape) { 499 checkNotNull(type,"Type URI cannot be null"); 500 checkNotNull(shape,"Shape cannot be null"); 501 checkArgument(!this.typeShapes.containsKey(type),"A shape is already defined for type '"+type+"'"); 502 this.typeShapes.put(type,shape); 503 return this; 504 } 505 506 public Constraints withNodeShape(Individual<?,?> individual, Shape shape) { 507 checkNotNull(individual,"Type URI cannot be null"); 508 checkNotNull(shape,"Shape cannot be null"); 509 checkArgument(!this.nodeShapes.containsKey(individual.id()),"A shape is already defined for individual '"+individual.id()+"'"); 510 this.nodeShapes.put(individual.id(),shape); 511 return this; 512 } 513 514 @Override 515 public String toString() { 516 return 517 MoreObjects. 518 toStringHelper(getClass()). 519 add("typeShapes", this.typeShapes). 520 add("nodeShapes", this.nodeShapes). 521 toString(); 522 } 523 524 public static Shape shape() { 525 return new Shape(); 526 } 527 528 public static Constraints constraints() { 529 return new Constraints(); 530 } 531 532 public static PropertyConstraint propertyConstraint(URI predicate) { 533 return new PropertyConstraint(predicate); 534 } 535 536 public static InversePropertyConstraint inversePropertyConstraint(URI predicate) { 537 return new InversePropertyConstraint(predicate); 538 } 539 540}