001/**
002 * Copyright 2015 DuraSpace, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.fcrepo.kernel.impl.rdf.impl;
017
018import com.google.common.base.Function;
019import com.google.common.base.Predicate;
020import com.google.common.collect.Iterators;
021import com.hp.hpl.jena.graph.Triple;
022import com.hp.hpl.jena.rdf.model.RDFNode;
023import com.hp.hpl.jena.rdf.model.Resource;
024
025import org.fcrepo.kernel.models.NonRdfSourceDescription;
026import org.fcrepo.kernel.models.FedoraResource;
027import org.fcrepo.kernel.exception.RepositoryRuntimeException;
028import org.fcrepo.kernel.identifiers.IdentifierConverter;
029import org.fcrepo.kernel.impl.rdf.converters.ValueConverter;
030import org.fcrepo.kernel.impl.rdf.impl.mappings.PropertyValueIterator;
031
032import org.slf4j.Logger;
033
034import javax.jcr.Node;
035import javax.jcr.Property;
036import javax.jcr.RepositoryException;
037import javax.jcr.Value;
038
039import java.util.Iterator;
040
041import static com.google.common.collect.Iterators.singletonIterator;
042import static com.hp.hpl.jena.graph.NodeFactory.createURI;
043import static com.hp.hpl.jena.graph.Triple.create;
044import static com.hp.hpl.jena.rdf.model.ResourceFactory.createResource;
045import static java.util.Collections.emptyIterator;
046import static org.fcrepo.kernel.FedoraJcrTypes.LDP_BASIC_CONTAINER;
047import static org.fcrepo.kernel.FedoraJcrTypes.LDP_DIRECT_CONTAINER;
048import static org.fcrepo.kernel.FedoraJcrTypes.LDP_HAS_MEMBER_RELATION;
049import static org.fcrepo.kernel.FedoraJcrTypes.LDP_INDIRECT_CONTAINER;
050import static org.fcrepo.kernel.FedoraJcrTypes.LDP_INSERTED_CONTENT_RELATION;
051import static org.fcrepo.kernel.FedoraJcrTypes.LDP_MEMBER_RESOURCE;
052import static org.fcrepo.kernel.RdfLexicon.LDP_MEMBER;
053import static org.fcrepo.kernel.RdfLexicon.MEMBER_SUBJECT;
054import static org.fcrepo.kernel.impl.identifiers.NodeResourceConverter.nodeConverter;
055import static org.fcrepo.kernel.impl.rdf.converters.PropertyConverter.getPropertyNameFromPredicate;
056import static org.fcrepo.kernel.impl.utils.FedoraTypesUtils.getReferencePropertyName;
057import static org.slf4j.LoggerFactory.getLogger;
058
059/**
060 * @author cabeer
061 * @author ajs6f
062 * @since 9/25/14
063 */
064public class LdpContainerRdfContext extends NodeRdfContext {
065    private static final Logger LOGGER = getLogger(ChildrenRdfContext.class);
066
067    /**
068     * Default constructor.
069     *
070     * @param resource the resource
071     * @param idTranslator the id translator
072     * @throws javax.jcr.RepositoryException if repository exception occurred
073     */
074    public LdpContainerRdfContext(final FedoraResource resource,
075                                  final IdentifierConverter<Resource, FedoraResource> idTranslator)
076            throws RepositoryException {
077        super(resource, idTranslator);
078        final Iterator<Property> memberReferences = resource.getNode().getReferences(LDP_MEMBER_RESOURCE);
079        final Iterator<Property> properties = Iterators.filter(memberReferences, isContainer );
080
081        if (properties.hasNext()) {
082            LOGGER.trace("Found membership containers for {}", resource);
083            concat(membershipContext(properties));
084        }
085    }
086
087    private static Predicate<Property> isContainer = new Predicate<Property>() {
088
089        @Override
090        public boolean apply(final Property property) {
091            try {
092                final Node container = property.getParent();
093                return container.isNodeType(LDP_DIRECT_CONTAINER) || container.isNodeType(LDP_INDIRECT_CONTAINER);
094            } catch (final RepositoryException e) {
095                throw new RepositoryRuntimeException(e);
096            }
097        }
098    };
099
100    private Iterator<Triple> membershipContext(final Iterator<Property> properties) {
101        return Iterators.concat(Iterators.transform(properties, nodes2triples()));
102    }
103
104    private Function<Property, Iterator<Triple>> nodes2triples() {
105        return new Function<Property,Iterator<Triple>>() {
106
107            @Override
108            public Iterator<Triple> apply(final Property property) {
109                try {
110                    final FedoraResource resource = nodeConverter.convert(property.getParent());
111                    return memberRelations(resource);
112                } catch (final RepositoryException e) {
113                    throw new RepositoryRuntimeException(e);
114                }
115            }
116        };
117    }
118
119    /**
120     * Get the member relations assert on the subject by the given node
121     * @param container
122     * @return
123     * @throws RepositoryException
124     */
125    private Iterator<Triple> memberRelations(final FedoraResource container) throws RepositoryException {
126        final com.hp.hpl.jena.graph.Node memberRelation;
127
128        if (container.hasProperty(LDP_HAS_MEMBER_RELATION)) {
129            final Property property = container.getProperty(LDP_HAS_MEMBER_RELATION);
130            memberRelation = createURI(property.getString());
131        } else if (container.hasType(LDP_BASIC_CONTAINER)) {
132            memberRelation = LDP_MEMBER.asNode();
133        } else {
134            return emptyIterator();
135        }
136
137        final String insertedContainerProperty;
138
139        if (container.hasType(LDP_INDIRECT_CONTAINER)) {
140            if (container.hasProperty(LDP_INSERTED_CONTENT_RELATION)) {
141                insertedContainerProperty = container.getProperty(LDP_INSERTED_CONTENT_RELATION).getString();
142            } else {
143                return emptyIterator();
144            }
145        } else {
146            insertedContainerProperty = MEMBER_SUBJECT.getURI();
147        }
148
149        final Iterator<FedoraResource> memberNodes = container.getChildren();
150
151        return Iterators.concat(Iterators.transform(memberNodes,
152                new FedoraResourceTripleFunction(insertedContainerProperty, memberRelation)));
153    }
154
155    private class FedoraResourceTripleFunction implements Function<FedoraResource, Iterator<Triple>>   {
156
157        private final String insertedContainerProperty;
158
159        private final com.hp.hpl.jena.graph.Node memberRelation;
160
161        public FedoraResourceTripleFunction(final String insertedContainerProperty,
162                                            final com.hp.hpl.jena.graph.Node memberRelation) {
163            this.insertedContainerProperty = insertedContainerProperty;
164            this.memberRelation = memberRelation;
165        }
166
167        @Override
168        public Iterator<Triple> apply(final FedoraResource child) {
169
170            try {
171                final com.hp.hpl.jena.graph.Node childSubject;
172                if (child instanceof NonRdfSourceDescription) {
173                    childSubject = translator().reverse()
174                            .convert(((NonRdfSourceDescription) child).getDescribedResource())
175                            .asNode();
176                } else {
177                    childSubject = translator().reverse().convert(child).asNode();
178                }
179
180                if (insertedContainerProperty.equals(MEMBER_SUBJECT.getURI())) {
181
182                    return singletonIterator(create(subject(), memberRelation, childSubject));
183                }
184                String insertedContentProperty = getPropertyNameFromPredicate(resource().getNode(),
185                        createResource(insertedContainerProperty),
186                        null);
187
188                if (child.hasProperty(insertedContentProperty)) {
189                    // do nothing, insertedContentProperty is good
190
191                } else if (child.hasProperty(getReferencePropertyName(insertedContentProperty))) {
192                    // The insertedContentProperty is a pseudo reference property
193                    insertedContentProperty = getReferencePropertyName(insertedContentProperty);
194
195                } else {
196                    // No property found!
197                    return emptyIterator();
198                }
199
200                final PropertyValueIterator values
201                        = new PropertyValueIterator(child.getProperty(insertedContentProperty));
202
203                return Iterators.transform(values, new Function<Value, Triple>() {
204                    @Override
205                    public Triple apply(final Value input) {
206                        final RDFNode membershipResource = new ValueConverter(session(), translator())
207                                .convert(input);
208                        return create(subject(), memberRelation, membershipResource.asNode());
209                    }
210                });
211            } catch (final RepositoryException e) {
212                throw new RepositoryRuntimeException(e);
213            }
214        }
215
216    }
217}