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 */
018package org.apache.xbean.recipe;
019
020import java.util.Collections;
021import java.util.List;
022import java.lang.reflect.Type;
023
024public 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}