001    /*
002     * JBoss, Home of Professional Open Source.
003     * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004     * as indicated by the @author tags. See the copyright.txt file in the
005     * distribution for a full listing of individual contributors. 
006     *
007     * This is free software; you can redistribute it and/or modify it
008     * under the terms of the GNU Lesser General Public License as
009     * published by the Free Software Foundation; either version 2.1 of
010     * the License, or (at your option) any later version.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this software; if not, write to the Free
019     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021     */
022    package org.jboss.dna.repository.sequencers;
023    
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.util.Collections;
027    import java.util.HashSet;
028    import java.util.Set;
029    import javax.jcr.Node;
030    import javax.jcr.PropertyIterator;
031    import javax.jcr.PropertyType;
032    import javax.jcr.RepositoryException;
033    import javax.jcr.Value;
034    import net.jcip.annotations.Immutable;
035    import org.jboss.dna.common.util.CheckArg;
036    import org.jboss.dna.common.util.Logger;
037    import org.jboss.dna.graph.properties.Name;
038    import org.jboss.dna.graph.properties.NamespaceRegistry;
039    import org.jboss.dna.graph.properties.Path;
040    import org.jboss.dna.graph.properties.Property;
041    import org.jboss.dna.graph.properties.ValueFactories;
042    import org.jboss.dna.graph.sequencers.SequencerContext;
043    import org.jboss.dna.graph.sequencers.StreamSequencer;
044    import org.jboss.dna.repository.RepositoryI18n;
045    import org.jboss.dna.repository.mimetype.MimeType;
046    import org.jboss.dna.repository.util.JcrExecutionContext;
047    
048    /**
049     * Contains context information that is passed to {@link StreamSequencer stream sequencers}, including information about the input
050     * node containing the data being sequenced.
051     * 
052     * @author John Verhaeg
053     */
054    @Immutable
055    public class SequencerNodeContext implements SequencerContext {
056    
057        private final javax.jcr.Property sequencedProperty;
058        private final ValueFactories factories;
059        private final Path path;
060        private final Set<Property> props;
061        private final JcrExecutionContext context;
062    
063        SequencerNodeContext( Node input,
064                              javax.jcr.Property sequencedProperty,
065                              JcrExecutionContext context ) throws RepositoryException {
066            assert input != null;
067            assert sequencedProperty != null;
068            assert context != null;
069            this.context = context;
070            this.sequencedProperty = sequencedProperty;
071            this.factories = context.getValueFactories();
072            // Translate JCR path and property values to DNA constructs and cache them to improve performance and prevent
073            // RepositoryException from being thrown by getters
074            // Note: getMimeType() will still operate lazily, and thus throw a SequencerException, since it is very intrusive and
075            // potentially slow-running.
076            path = factories.getPathFactory().create(input.getPath());
077            Set<Property> props = new HashSet<Property>();
078            for (PropertyIterator iter = input.getProperties(); iter.hasNext();) {
079                javax.jcr.Property jcrProp = iter.nextProperty();
080                Property prop;
081                if (jcrProp.getDefinition().isMultiple()) {
082                    Value[] jcrVals = jcrProp.getValues();
083                    Object[] vals = new Object[jcrVals.length];
084                    int ndx = 0;
085                    for (Value jcrVal : jcrVals) {
086                        vals[ndx++] = convert(factories, jcrProp.getName(), jcrVal);
087                    }
088                    prop = context.getPropertyFactory().create(factories.getNameFactory().create(jcrProp.getName()), vals);
089                } else {
090                    Value jcrVal = jcrProp.getValue();
091                    Object val = convert(factories, jcrProp.getName(), jcrVal);
092                    prop = context.getPropertyFactory().create(factories.getNameFactory().create(jcrProp.getName()), val);
093                }
094                props.add(prop);
095            }
096            this.props = Collections.unmodifiableSet(props);
097        }
098    
099        private Object convert( ValueFactories factories,
100                                String name,
101                                Value jcrValue ) throws RepositoryException {
102            switch (jcrValue.getType()) {
103                case PropertyType.BINARY: {
104                    return factories.getBinaryFactory().create(jcrValue.getStream());
105                }
106                case PropertyType.BOOLEAN: {
107                    return factories.getBooleanFactory().create(jcrValue.getBoolean());
108                }
109                case PropertyType.DATE: {
110                    return factories.getDateFactory().create(jcrValue.getDate());
111                }
112                case PropertyType.DOUBLE: {
113                    return factories.getDoubleFactory().create(jcrValue.getDouble());
114                }
115                case PropertyType.LONG: {
116                    return factories.getLongFactory().create(jcrValue.getLong());
117                }
118                case PropertyType.NAME: {
119                    return factories.getNameFactory().create(jcrValue.getString());
120                }
121                case PropertyType.PATH: {
122                    return factories.getPathFactory().create(jcrValue.getString());
123                }
124                case PropertyType.REFERENCE: {
125                    return factories.getReferenceFactory().create(jcrValue.getString());
126                }
127                case PropertyType.STRING: {
128                    return factories.getStringFactory().create(jcrValue.getString());
129                }
130                default: {
131                    throw new RepositoryException(RepositoryI18n.unknownPropertyValueType.text(name, jcrValue.getType()));
132                }
133            }
134        }
135    
136        /**
137         * {@inheritDoc}
138         */
139        public ValueFactories getFactories() {
140            return factories;
141        }
142    
143        /**
144         * {@inheritDoc}
145         * 
146         * @see org.jboss.dna.graph.sequencers.SequencerContext#getInputPath()
147         */
148        public Path getInputPath() {
149            return path;
150        }
151    
152        /**
153         * {@inheritDoc}
154         * 
155         * @see org.jboss.dna.graph.sequencers.SequencerContext#getInputProperties()
156         */
157        public Set<Property> getInputProperties() {
158            return props;
159        }
160    
161        /**
162         * {@inheritDoc}
163         * 
164         * @see org.jboss.dna.graph.sequencers.SequencerContext#getInputProperty(org.jboss.dna.graph.properties.Name)
165         */
166        public Property getInputProperty( Name name ) {
167            CheckArg.isNotNull(name, "name");
168            for (Property prop : props) {
169                if (name.equals(prop.getName())) {
170                    return prop;
171                }
172            }
173            return null;
174        }
175    
176        /**
177         * {@inheritDoc}
178         * 
179         * @see org.jboss.dna.graph.sequencers.SequencerContext#getMimeType()
180         */
181        @SuppressWarnings( "null" )
182        // The need for the SuppressWarnings looks like an Eclipse bug
183        public String getMimeType() {
184            SequencerException err = null;
185            String mimeType = null;
186            InputStream stream = null;
187            try {
188                stream = sequencedProperty.getStream();
189                mimeType = MimeType.of(path.getLastSegment().getName().getLocalName(), stream);
190                return mimeType;
191            } catch (Exception error) {
192                err = new SequencerException(error);
193            } finally {
194                if (stream != null) {
195                    try {
196                        stream.close();
197                    } catch (IOException error) {
198                        // Only throw exception if an exception was not already thrown
199                        if (err == null) err = new SequencerException(error);
200                    }
201                }
202            }
203            if (err != null) throw err;
204            return mimeType;
205        }
206    
207        /**
208         * {@inheritDoc}
209         */
210        public NamespaceRegistry getNamespaceRegistry() {
211            return factories.getNameFactory().getNamespaceRegistry();
212        }
213    
214        /**
215         * {@inheritDoc}
216         * 
217         * @see org.jboss.dna.graph.sequencers.SequencerContext#getLogger(java.lang.Class)
218         */
219        public Logger getLogger( Class<?> clazz ) {
220            return context.getLogger(clazz);
221        }
222    
223        /**
224         * {@inheritDoc}
225         * 
226         * @see org.jboss.dna.graph.sequencers.SequencerContext#getLogger(java.lang.String)
227         */
228        public Logger getLogger( String name ) {
229            return context.getLogger(name);
230        }
231    }