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.security.AccessControlContext;
027    import java.util.Collections;
028    import java.util.HashSet;
029    import java.util.Set;
030    import javax.jcr.Node;
031    import javax.jcr.PropertyIterator;
032    import javax.jcr.PropertyType;
033    import javax.jcr.RepositoryException;
034    import javax.jcr.Value;
035    import javax.security.auth.Subject;
036    import javax.security.auth.login.LoginContext;
037    import net.jcip.annotations.Immutable;
038    import org.jboss.dna.common.collection.Problems;
039    import org.jboss.dna.common.util.CheckArg;
040    import org.jboss.dna.common.util.Logger;
041    import org.jboss.dna.graph.properties.Name;
042    import org.jboss.dna.graph.properties.NamespaceRegistry;
043    import org.jboss.dna.graph.properties.Path;
044    import org.jboss.dna.graph.properties.Property;
045    import org.jboss.dna.graph.properties.PropertyFactory;
046    import org.jboss.dna.graph.properties.ValueFactories;
047    import org.jboss.dna.graph.sequencers.SequencerContext;
048    import org.jboss.dna.graph.sequencers.StreamSequencer;
049    import org.jboss.dna.repository.RepositoryI18n;
050    import org.jboss.dna.repository.mimetype.MimeType;
051    import org.jboss.dna.repository.util.JcrExecutionContext;
052    
053    /**
054     * Contains context information that is passed to {@link StreamSequencer stream sequencers}, including information about the input
055     * node containing the data being sequenced.
056     * 
057     * @author John Verhaeg
058     */
059    @Immutable
060    public class SequencerNodeContext implements SequencerContext {
061    
062        private final javax.jcr.Property sequencedProperty;
063        private final ValueFactories factories;
064        private final Path path;
065        private final Set<Property> props;
066        private final JcrExecutionContext context;
067        private final Problems problems;
068    
069        SequencerNodeContext( Node input,
070                              javax.jcr.Property sequencedProperty,
071                              JcrExecutionContext context,
072                              Problems problems ) throws RepositoryException {
073            assert input != null;
074            assert sequencedProperty != null;
075            assert context != null;
076            assert problems != null;
077            this.context = context;
078            this.sequencedProperty = sequencedProperty;
079            this.problems = problems;
080            this.factories = context.getValueFactories();
081            // Translate JCR path and property values to DNA constructs and cache them to improve performance and prevent
082            // RepositoryException from being thrown by getters
083            // Note: getMimeType() will still operate lazily, and thus throw a SequencerException, since it is very intrusive and
084            // potentially slow-running.
085            path = factories.getPathFactory().create(input.getPath());
086            Set<Property> props = new HashSet<Property>();
087            for (PropertyIterator iter = input.getProperties(); iter.hasNext();) {
088                javax.jcr.Property jcrProp = iter.nextProperty();
089                Property prop;
090                if (jcrProp.getDefinition().isMultiple()) {
091                    Value[] jcrVals = jcrProp.getValues();
092                    Object[] vals = new Object[jcrVals.length];
093                    int ndx = 0;
094                    for (Value jcrVal : jcrVals) {
095                        vals[ndx++] = convert(factories, jcrProp.getName(), jcrVal);
096                    }
097                    prop = context.getPropertyFactory().create(factories.getNameFactory().create(jcrProp.getName()), vals);
098                } else {
099                    Value jcrVal = jcrProp.getValue();
100                    Object val = convert(factories, jcrProp.getName(), jcrVal);
101                    prop = context.getPropertyFactory().create(factories.getNameFactory().create(jcrProp.getName()), val);
102                }
103                props.add(prop);
104            }
105            this.props = Collections.unmodifiableSet(props);
106        }
107    
108        private Object convert( ValueFactories factories,
109                                String name,
110                                Value jcrValue ) throws RepositoryException {
111            switch (jcrValue.getType()) {
112                case PropertyType.BINARY: {
113                    return factories.getBinaryFactory().create(jcrValue.getStream());
114                }
115                case PropertyType.BOOLEAN: {
116                    return factories.getBooleanFactory().create(jcrValue.getBoolean());
117                }
118                case PropertyType.DATE: {
119                    return factories.getDateFactory().create(jcrValue.getDate());
120                }
121                case PropertyType.DOUBLE: {
122                    return factories.getDoubleFactory().create(jcrValue.getDouble());
123                }
124                case PropertyType.LONG: {
125                    return factories.getLongFactory().create(jcrValue.getLong());
126                }
127                case PropertyType.NAME: {
128                    return factories.getNameFactory().create(jcrValue.getString());
129                }
130                case PropertyType.PATH: {
131                    return factories.getPathFactory().create(jcrValue.getString());
132                }
133                case PropertyType.REFERENCE: {
134                    return factories.getReferenceFactory().create(jcrValue.getString());
135                }
136                case PropertyType.STRING: {
137                    return factories.getStringFactory().create(jcrValue.getString());
138                }
139                default: {
140                    throw new RepositoryException(RepositoryI18n.unknownPropertyValueType.text(name, jcrValue.getType()));
141                }
142            }
143        }
144    
145        /**
146         * {@inheritDoc}
147         * 
148         * @see org.jboss.dna.graph.ExecutionContext#getAccessControlContext()
149         */
150        public AccessControlContext getAccessControlContext() {
151            return context.getAccessControlContext();
152        }
153    
154        /**
155         * {@inheritDoc}
156         * 
157         * @see org.jboss.dna.common.component.ClassLoaderFactory#getClassLoader(java.lang.String[])
158         */
159        public ClassLoader getClassLoader( String... classpath ) {
160            return context.getClassLoader(classpath);
161        }
162    
163        /**
164         * {@inheritDoc}
165         */
166        public ValueFactories getValueFactories() {
167            return factories;
168        }
169    
170        /**
171         * {@inheritDoc}
172         * 
173         * @see org.jboss.dna.graph.sequencers.SequencerContext#getInputPath()
174         */
175        public Path getInputPath() {
176            return path;
177        }
178    
179        /**
180         * {@inheritDoc}
181         * 
182         * @see org.jboss.dna.graph.sequencers.SequencerContext#getInputProperties()
183         */
184        public Set<Property> getInputProperties() {
185            return props;
186        }
187    
188        /**
189         * {@inheritDoc}
190         * 
191         * @see org.jboss.dna.graph.sequencers.SequencerContext#getInputProperty(org.jboss.dna.graph.properties.Name)
192         */
193        public Property getInputProperty( Name name ) {
194            CheckArg.isNotNull(name, "name");
195            for (Property prop : props) {
196                if (name.equals(prop.getName())) {
197                    return prop;
198                }
199            }
200            return null;
201        }
202    
203        /**
204         * {@inheritDoc}
205         * 
206         * @see org.jboss.dna.graph.sequencers.SequencerContext#getProblems()
207         */
208        public Problems getProblems() {
209            return problems;
210        }
211    
212        /**
213         * {@inheritDoc}
214         * 
215         * @see org.jboss.dna.graph.sequencers.SequencerContext#getMimeType()
216         */
217        @SuppressWarnings( "null" )
218        // The need for the SuppressWarnings looks like an Eclipse bug
219        public String getMimeType() {
220            SequencerException err = null;
221            String mimeType = null;
222            InputStream stream = null;
223            try {
224                stream = sequencedProperty.getStream();
225                mimeType = MimeType.of(path.getLastSegment().getName().getLocalName(), stream);
226                return mimeType;
227            } catch (Exception error) {
228                err = new SequencerException(error);
229            } finally {
230                if (stream != null) {
231                    try {
232                        stream.close();
233                    } catch (IOException error) {
234                        // Only throw exception if an exception was not already thrown
235                        if (err == null) err = new SequencerException(error);
236                    }
237                }
238            }
239            if (err != null) throw err;
240            return mimeType;
241        }
242    
243        /**
244         * {@inheritDoc}
245         * 
246         * @see org.jboss.dna.graph.sequencers.SequencerContext#getLogger(java.lang.Class)
247         */
248        public Logger getLogger( Class<?> clazz ) {
249            return context.getLogger(clazz);
250        }
251    
252        /**
253         * {@inheritDoc}
254         * 
255         * @see org.jboss.dna.graph.sequencers.SequencerContext#getLogger(java.lang.String)
256         */
257        public Logger getLogger( String name ) {
258            return context.getLogger(name);
259        }
260    
261        /**
262         * {@inheritDoc}
263         * 
264         * @see org.jboss.dna.graph.ExecutionContext#getLoginContext()
265         */
266        public LoginContext getLoginContext() {
267            return context.getLoginContext();
268        }
269    
270        /**
271         * {@inheritDoc}
272         */
273        public NamespaceRegistry getNamespaceRegistry() {
274            return factories.getNameFactory().getNamespaceRegistry();
275        }
276    
277        /**
278         * {@inheritDoc}
279         * 
280         * @see org.jboss.dna.graph.ExecutionContext#getPropertyFactory()
281         */
282        public PropertyFactory getPropertyFactory() {
283            return context.getPropertyFactory();
284        }
285    
286        /**
287         * {@inheritDoc}
288         * 
289         * @see org.jboss.dna.graph.ExecutionContext#getSubject()
290         */
291        public Subject getSubject() {
292            return context.getSubject();
293        }
294    }