/** * Subclasses can override this method to cater to their special needs for * UndeterminedRelationships by default, it expects something like this in the original EAD: * * <p> * * <pre>{@code * <persname source="terezin-victims" authfilenumber="PERSON.ITI.1514982">Kien, * Leonhard (* 11.5.1886)</persname> * }</pre> * * <p>it works in unison with the extractRelations() method. * * @param unit the current unit */ protected void solveUndeterminedRelationships(DocumentaryUnit unit) throws ValidationError { // Try to resolve the undetermined relationships // we can only create the annotations after the DocumentaryUnit // and its Description have been added to the graph, // so they have IDs. Api api = ApiFactory.noLogging(framedGraph, actioner.as(UserProfile.class)); Bundle linkBundle = new Bundle(EntityClass.LINK) .withDataValue(Ontology.LINK_HAS_DESCRIPTION, RESOLVED_LINK_DESC); for (Description desc : unit.getDescriptions()) { // Put the set of relationships into a HashSet to remove duplicates. for (AccessPoint rel : Sets.newHashSet(desc.getAccessPoints())) { // the wp2 undetermined relationship that can be resolved have a 'cvoc' and a 'concept' // attribute. // they need to be found in the vocabularies that are in the graph if (rel.getPropertyKeys().contains("cvoc")) { String vocab = rel.getProperty("cvoc"); String conceptId = rel.getProperty("concept"); if (conceptId == null) { conceptId = rel.getProperty("target"); } logger.debug("cvoc: {}, concept: {}", vocab, conceptId); try { Vocabulary vocabulary = manager.getEntity(vocab, Vocabulary.class); for (Concept concept : vocabulary.getConcepts()) { logger.debug("********************* {} {}", concept.getId(), concept.getIdentifier()); if (concept.getIdentifier().equalsIgnoreCase(conceptId)) { try { // TODO: Fix link type here... Bundle data = linkBundle.withDataValue(Ontology.LINK_HAS_TYPE, "associative"); Link link = api.create(data, Link.class); unit.addLink(link); concept.addLink(link); link.addLinkBody(rel); logger.debug("link created between {} and {}", conceptId, concept.getId()); } catch (PermissionDenied | DeserializationError ex) { logger.error(ex.getMessage()); } } } } catch (ItemNotFound ex) { logger.error("Vocabulary with id {} not found: {}", vocab, ex.getMessage()); } } else { logger.debug("no cvoc found"); } } } }
@Override @Before public void setUp() throws Exception { super.setUp(); instance = IdentifiableEntityIdGenerator.INSTANCE; scopes = Lists.newArrayList("r1"); bundle = Bundle.fromData(TestData.getTestDocBundle()); }
/** * Import a single archdesc or c01-12 item, keeping a reference to the hierarchical depth. * * @param itemData The raw data map * @param idPath The identifiers of parent documents, not including those of the overall * permission scope * @throws ValidationError when the itemData does not contain an identifier for the unit or... */ @Override public AbstractUnit importItem(Map<String, Object> itemData, List<String> idPath) throws ValidationError { BundleManager persister = getPersister(idPath); Bundle description = getDescription(itemData); // extractDocumentaryUnit does not throw ValidationError on missing ID Bundle unit = new Bundle(unitEntity, extractDocumentaryUnit(itemData)); // Check for missing identifier, throw an exception when there is no ID. if (unit.getDataValue(Ontology.IDENTIFIER_KEY) == null) { throw new ValidationError( unit, Ontology.IDENTIFIER_KEY, "Missing identifier " + Ontology.IDENTIFIER_KEY); } logger.debug("Imported item: {}", itemData.get("name")); Mutation<DocumentaryUnit> mutation = persister.createOrUpdate( mergeWithPreviousAndSave(unit, description, idPath), DocumentaryUnit.class); DocumentaryUnit frame = mutation.getNode(); // Set the repository/item relationship if (idPath.isEmpty() && mutation.created()) { EntityClass scopeType = manager.getEntityClass(permissionScope); if (scopeType.equals(EntityClass.REPOSITORY)) { Repository repository = framedGraph.frame(permissionScope.asVertex(), Repository.class); frame.setRepository(repository); frame.setPermissionScope(repository); } else if (scopeType.equals(unitEntity)) { DocumentaryUnit parent = framedGraph.frame(permissionScope.asVertex(), DocumentaryUnit.class); parent.addChild(frame); frame.setPermissionScope(parent); } else { logger.error("Unknown scope type for documentary unit: {}", scopeType); } } handleCallbacks(mutation); logger.debug("============== {} state: {}", frame.getId(), mutation.getState()); if (mutation.created()) { solveUndeterminedRelationships(frame); } return frame; }
/** * Finds any bundle in the graph with the same ObjectIdentifier. If there is no bundle with this * identifier, it is created. If it exists and a Description in the given language exists from the * same source file, the description is replaced. If the description is from another source, it is * added to the bundle's descriptions. * * @param unit the DocumentaryUnit to be saved * @param descBundle the documentsDescription to replace any previous ones with this language * @param idPath the ID path of this bundle (will be relative to the ID path of the permission * scope) * @return A bundle with description relationships merged. */ protected Bundle mergeWithPreviousAndSave(Bundle unit, Bundle descBundle, List<String> idPath) throws ValidationError { final String languageOfDesc = descBundle.getDataValue(Ontology.LANGUAGE_OF_DESCRIPTION); final String thisSourceFileId = descBundle.getDataValue(Ontology.SOURCEFILE_KEY); logger.debug( "merging: descBundle's language = {}, sourceFileId = {}", languageOfDesc, thisSourceFileId); /* * for some reason, the idpath from the permissionscope does not contain the parent documentary unit. * TODO: so for now, it is added manually */ List<String> itemIdPath = Lists.newArrayList(getPermissionScope().idPath()); itemIdPath.addAll(idPath); Bundle unitWithIds = unit.generateIds(itemIdPath); logger.debug("merging: docUnit's graph id = {}", unitWithIds.getId()); // If the bundle exists, we merge if (manager.exists(unitWithIds.getId())) { try { // read the current item’s bundle Bundle oldBundle = mergeSerializer.vertexToBundle(manager.getVertex(unitWithIds.getId())); // filter out dependents that a) are descriptions, b) have the same language/code, // and c) have the same source file ID BiPredicate<String, Bundle> filter = (relationLabel, bundle) -> { String lang = bundle.getDataValue(Ontology.LANGUAGE); String oldSourceFileId = bundle.getDataValue(Ontology.SOURCEFILE_KEY); return relationLabel.equals(Ontology.DESCRIPTION_FOR_ENTITY) && bundle.getType().equals(EntityClass.DOCUMENTARY_UNIT_DESCRIPTION) && (lang != null && lang.equals(languageOfDesc)) && (oldSourceFileId != null && oldSourceFileId.equals(thisSourceFileId)); }; Bundle filtered = oldBundle.filterRelations(filter); return unitWithIds .withRelations(filtered.getRelations()) .withRelation(Ontology.DESCRIPTION_FOR_ENTITY, descBundle); } catch (SerializationError ex) { throw new ValidationError(unit, "serialization error", ex.getMessage()); } catch (ItemNotFound ex) { throw new ValidationError(unit, "item not found exception", ex.getMessage()); } } else { // else we create a new bundle. return unit.withRelation(Ontology.DESCRIPTION_FOR_ENTITY, descBundle); } }