/* * Copyright 2013 Atteo. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.atteo.moonshine.services.internal; import java.lang.annotation.Annotation; import java.util.List; import org.atteo.moonshine.services.ImportService; import org.atteo.moonshine.services.Service; import com.google.common.base.Strings; import com.google.inject.Binder; import com.google.inject.Binding; import com.google.inject.Key; import com.google.inject.Module; import com.google.inject.PrivateBinder; import com.google.inject.name.Names; import com.google.inject.spi.DefaultElementVisitor; import com.google.inject.spi.Element; import com.google.inject.spi.Elements; import com.google.inject.spi.InjectionRequest; import com.google.inject.spi.PrivateElements; public class ServiceModuleRewriter { private ServiceModuleRewriter() { } /** * Annotate exposed bindings with Names.named(service.getId()). * Additionally request injection of service object. */ public static List<Element> annotateExposedWithId(final List<Element> elements, final Service service) { final boolean singleton = ReflectionTools.isSingleton(service.getClass()); return Elements.getElements((Module) (final Binder binder) -> { final PrivateBinder privateBinder = binder.newPrivateBinder(); privateBinder.requestInjection(service); for (Element element : elements) { element.acceptVisitor(new DefaultElementVisitor<Void>() { @Override public Void visit(PrivateElements privateElements) { annotateExposedWithId(privateBinder, privateElements, service); return null; } @Override public <T> Void visit(Binding<T> binding) { binding.applyTo(privateBinder); Key<T> oldKey = binding.getKey(); if (!singleton && !Strings.isNullOrEmpty(service.getId())) { bindKey(privateBinder, oldKey, Names.named(service.getId())); } else { privateBinder.expose(oldKey); } return null; } @Override protected Void visitOther(Element element) { element.applyTo(binder); return null; } }); } }); } private static <T> void bindKey(PrivateBinder binder, Key<T> oldKey, Annotation annotation) { Key<T> newKey = Key.get(oldKey.getTypeLiteral(), annotation); binder.bind(newKey).to(oldKey); binder.expose(newKey); } private static void annotateExposedWithId(final PrivateBinder binder, final PrivateElements elements, final Service service) { final boolean singleton = ReflectionTools.isSingleton(service.getClass()); // rewrite all bindings for (Element element : elements.getElements()) { element.applyTo(binder); } // rewrite all exposed bindings to include id for (Key<?> oldKey : elements.getExposedKeys()) { if (!singleton && !Strings.isNullOrEmpty(service.getId())) { bindKey(binder, oldKey, Names.named(service.getId())); } else { binder.expose(oldKey); } } } /** * Create a module with all provided elements and also with bindings imported * with {@link ImportService} annotation. * @param service service to scan for {@link ImportService} annotation * @param services list of all services * @param hints list where hints will be stored * @return provided elements with bindings imported from given service */ public static List<Element> importBindings(final ServiceWrapper service, final List<ServiceWrapper> services, final List<String> hints) { return Elements.getElements((Module) (final Binder binder) -> { final PrivateBinder privateBinder = binder.newPrivateBinder(); for (Element element : service.getElements()) { element.acceptVisitor(new DefaultElementVisitor<Void>() { @Override public Void visit(PrivateElements privateElements) { importBindings(privateBinder, privateElements, service, services, hints); return null; } @Override public Void visit(InjectionRequest<?> injectionRequest) { injectionRequest.applyTo(privateBinder); return null; } @Override protected Void visitOther(Element element) { // copy all elements element.applyTo(binder); return null; } }); } }); } private static void importBindings(final PrivateBinder binder, PrivateElements elements, ServiceWrapper service, List<ServiceWrapper> services, List<String> hints) { // copy all elements for (Element element : elements.getElements()) { element.applyTo(binder); } for (Key<?> key : elements.getExposedKeys()) { binder.expose(key); } for (final ServiceWrapper.Dependency dependency : service.getDependencies()) { List<Element> importedElements; importedElements = dependency.getService().getElements(); if (importedElements == null) { throw new RuntimeException("Imported service does not specify any module"); } for (final Element element : importedElements) { element.acceptVisitor(new DefaultElementVisitor<Void>() { private <T> void bindKey(Key<T> key) { Key<T> sourceKey; Class<? extends Annotation> annotation = dependency.getAnnotation(); if (annotation == ImportService.NoAnnotation.class) { sourceKey = Key.get(key.getTypeLiteral()); } else { sourceKey = Key.get(key.getTypeLiteral(), annotation); } if (!sourceKey.equals(key)) { binder.withSource(element.getSource()).bind(sourceKey).to(key); } } @Override public Void visit(PrivateElements privateElements) { for (Key<?> key : privateElements.getExposedKeys()) { bindKey(key); } return null; } @Override public <T> Void visit(Binding<T> binding) { bindKey(binding.getKey()); return null; } }); } } } }