001    /**
002     *
003     * Licensed to the Apache Software Foundation (ASF) under one or more
004     * contributor license agreements.  See the NOTICE file distributed with
005     * this work for additional information regarding copyright ownership.
006     * The ASF licenses this file to You under the Apache License, Version 2.0
007     * (the "License"); you may not use this file except in compliance with
008     * the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    package org.apache.xbean.recipe;
019    
020    import java.util.Collections;
021    import java.util.List;
022    import java.lang.reflect.Type;
023    
024    public class ReferenceRecipe extends AbstractRecipe {
025        private String referenceName;
026    
027        public ReferenceRecipe() {
028        }
029    
030        public ReferenceRecipe(String referenceName) {
031            this.referenceName = referenceName;
032        }
033    
034        public String getReferenceName() {
035            return referenceName;
036        }
037    
038        public void setReferenceName(String name) {
039            this.referenceName = name;
040        }
041    
042        public List<Recipe> getNestedRecipes() {
043            ExecutionContext context = ExecutionContext.getContext();
044            if (!context.containsObject(referenceName)) {
045                throw new NoSuchObjectException(referenceName);
046            }
047    
048            Object object = ExecutionContext.getContext().getObject(referenceName);
049            if (object instanceof Recipe) {
050                Recipe recipe = (Recipe) object;
051                return Collections.singletonList(recipe);
052            }
053            return Collections.emptyList();
054        }
055    
056        public List<Recipe> getConstructorRecipes() {
057            return getNestedRecipes();
058        }
059    
060        public boolean canCreate(Type type) {
061            if (referenceName == null) {
062                throw new ConstructionException("Reference name has not been set");
063            }
064    
065            ExecutionContext context = ExecutionContext.getContext();
066    
067            Object object = context.getObject(referenceName);
068            if (object instanceof Recipe) {
069                Recipe recipe = (Recipe) object;
070                return recipe.canCreate(type);
071            } else {
072                return RecipeHelper.isInstance(type, object);
073            }
074        }
075    
076        protected Object internalCreate(Type expectedType, boolean lazyRefAllowed) throws ConstructionException {
077            if (referenceName == null) {
078                throw new ConstructionException("Reference name has not been set");
079            }
080    
081            ExecutionContext context = ExecutionContext.getContext();
082    
083            Object object;
084            if (!context.containsObject(referenceName)) {
085                if (!lazyRefAllowed) {
086                    throw new ConstructionException("Currently no object registered with name " + referenceName +
087                            " and a lazy reference not allowed");
088                }
089    
090                Reference reference = new Reference(referenceName);
091                context.addReference(reference);
092                object = reference;
093            } else {
094                object = context.getObject(referenceName);
095                if (object instanceof Recipe) {
096                    if (lazyRefAllowed) {
097                        Reference reference = new Reference(referenceName);
098                        context.addReference(reference);
099                        object = reference;
100                    } else {
101                        Recipe recipe = (Recipe) object;
102                        object = recipe.create(expectedType, false);
103                    }
104    
105                }
106            }
107    
108            // add to execution context if name is specified
109            if (getName() != null) {
110                if (object instanceof Reference) {
111                    object = new WrapperReference(getName(), (Reference) object);
112                } else {
113                    ExecutionContext.getContext().addObject(getName(), object);
114                }
115            }
116    
117            return object;
118        }
119    
120        private static class WrapperReference extends Reference {
121            private final Reference delegate;
122    
123            private WrapperReference(String name, Reference delegate) {
124                super(name);
125                this.delegate = delegate;
126            }
127    
128            public boolean isResolved() {
129                return delegate.isResolved();
130            }
131    
132            public Object get() {
133                return delegate.get();
134            }
135    
136            public void set(Object object) {
137                if (isResolved()) {
138                    throw new ConstructionException("Reference has already been resolved");
139                }
140    
141                // add to execution context
142                ExecutionContext.getContext().addObject(getName(), object);
143    
144                delegate.set(object);
145            }
146    
147            public void setAction(Action action) {
148                delegate.setAction(action);
149            }
150        }
151    }