Package org.jboss.errai.ioc.rebind.ioc.injector.api

Source Code of org.jboss.errai.ioc.rebind.ioc.injector.api.InjectableInstance$TransientDataHolder

/*
* Copyright 2011 JBoss, by Red Hat, Inc
*
* 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.jboss.errai.ioc.rebind.ioc.injector.api;


import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.jboss.errai.codegen.Statement;
import org.jboss.errai.codegen.literal.LiteralFactory;
import org.jboss.errai.codegen.meta.MetaClass;
import org.jboss.errai.codegen.meta.MetaClassFactory;
import org.jboss.errai.codegen.meta.MetaConstructor;
import org.jboss.errai.codegen.meta.MetaField;
import org.jboss.errai.codegen.meta.MetaMethod;
import org.jboss.errai.codegen.meta.MetaParameter;
import org.jboss.errai.codegen.util.Refs;
import org.jboss.errai.codegen.util.Stmt;
import org.jboss.errai.ioc.client.api.qualifiers.BuiltInQualifiers;
import org.jboss.errai.ioc.client.container.BeanProvider;
import org.jboss.errai.ioc.client.container.RefHolder;
import org.jboss.errai.ioc.rebind.ioc.bootstrapper.IOCProcessingContext;
import org.jboss.errai.ioc.rebind.ioc.exception.InjectionFailure;
import org.jboss.errai.ioc.rebind.ioc.injector.AsyncInjectUtil;
import org.jboss.errai.ioc.rebind.ioc.injector.InjectUtil;
import org.jboss.errai.ioc.rebind.ioc.injector.Injector;
import org.jboss.errai.ioc.rebind.ioc.injector.basic.TypeInjector;
import org.jboss.errai.ioc.rebind.ioc.metadata.JSR330QualifyingMetadata;

public class InjectableInstance<T extends Annotation> extends InjectionPoint<T> {
  private static final String TRANSIENT_DATA_KEY = "InjectableInstance::TransientData";
  private static final TransientDataHolder EMPTY_HOLDER = TransientDataHolder.makeEmpty();

  private static class TransientDataHolder {
    private final Map<String, Map<MetaClass, Statement>> unsatisfiedTransients;
    private final Map<String, Map<MetaClass, Statement>> transientValues;

    private TransientDataHolder(Map<String, Map<MetaClass, Statement>> unsatisfiedTransients, Map<String, Map<MetaClass, Statement>> transientValues) {
      this.unsatisfiedTransients = unsatisfiedTransients;
      this.transientValues = transientValues;
    }

    static TransientDataHolder makeEmpty() {
      return new TransientDataHolder(Collections.<String, Map<MetaClass, Statement>>emptyMap(), Collections.<String, Map<MetaClass, Statement>>emptyMap());
    }

    static TransientDataHolder makeNew() {
      return new TransientDataHolder(new HashMap<String, Map<MetaClass, Statement>>(), new HashMap<String, Map<MetaClass, Statement>>());
    }
  }

  public InjectableInstance(final T annotation,
                            final TaskType taskType,
                            final MetaConstructor constructor,
                            final MetaMethod method,
                            final MetaField field,
                            final MetaClass type,
                            final MetaParameter parm,
                            final Injector injector,
                            final InjectionContext injectionContext) {

    super(annotation, taskType, constructor, method, field, type, parm, injector, injectionContext);
  }

  public static <T extends Annotation> InjectableInstance<T> getInjectedInstance(final T annotation,
                                                                                 final MetaClass type,
                                                                                 final Injector injector,
                                                                                 final InjectionContext context) {
    return new InjectableInstance<T>(annotation, TaskType.Type, null, null, null, type,
        null, injector, context);

  }

  public static <T extends Annotation> InjectableInstance<T> getMethodInjectedInstance(final MetaMethod method,
                                                                                       final Injector injector,
                                                                                       final InjectionContext context) {

    //noinspection unchecked
    return new InjectableInstance(
        context.getMatchingAnnotationForElementType(WiringElementType.InjectionPoint, method),
        !method.isPublic() ? TaskType.PrivateMethod : TaskType.Method,
        null,
        method,
        null,
        method.getDeclaringClass(),
        null,
        injector,
        context);

  }

  public static <T extends Annotation> InjectableInstance<T> getParameterInjectedInstance(final MetaParameter parm,
                                                                                          final Injector injector,
                                                                                          final InjectionContext context) {

    if (parm.getDeclaringMember() instanceof MetaConstructor) {

      //noinspection unchecked
      return new InjectableInstance(
          context.getMatchingAnnotationForElementType(WiringElementType.InjectionPoint,
              parm.getDeclaringMember()),
          TaskType.Parameter,
          ((MetaConstructor) parm.getDeclaringMember()),
          null,
          null,
          parm.getDeclaringMember().getDeclaringClass(),
          parm,
          injector,
          context);
    }
    else {
      //noinspection unchecked
      return new InjectableInstance(
          context.getMatchingAnnotationForElementType(WiringElementType.InjectionPoint,
              parm.getDeclaringMember()),
          TaskType.Parameter,
          null,
          ((MetaMethod) parm.getDeclaringMember()),
          null,
          parm.getDeclaringMember().getDeclaringClass(),
          parm,
          injector,
          context);
    }
  }

  public static <T extends Annotation> InjectableInstance<T> getFieldInjectedInstance(final MetaField field,
                                                                                      final Injector injector,
                                                                                      final InjectionContext context) {

    //noinspection unchecked
    return new InjectableInstance(
        context.getMatchingAnnotationForElementType(WiringElementType.InjectionPoint, field),
        !field.isPublic() ? TaskType.PrivateField : TaskType.Field,
        null,
        null,
        field,
        field.getDeclaringClass(),
        null,
        injector,
        context);

  }

  private TransientDataHolder getTransientDataHolder() {
    if (!getTargetInjector().hasAttribute(TRANSIENT_DATA_KEY)) {
      return EMPTY_HOLDER;
    }
    else {
      return (TransientDataHolder) getTargetInjector().getAttribute(TRANSIENT_DATA_KEY);
    }
  }

  private TransientDataHolder getOrCreateWritableDataHolder() {
    if (!getTargetInjector().hasAttribute(TRANSIENT_DATA_KEY)) {
      final TransientDataHolder holder = TransientDataHolder.makeNew();
      getTargetInjector().setAttribute(TRANSIENT_DATA_KEY, holder);
      return holder;
    }
    else {
      return (TransientDataHolder) getTargetInjector().getAttribute(TRANSIENT_DATA_KEY);
    }
  }

  /**
   * Record a transient value -- ie. a value we want the IOC container to track and be referenceable
   * while wiring the code, but not something that is injected.
   */
  public void addTransientValue(final String name, final Class type, final Statement valueRef) {
    addTransientValue(name, MetaClassFactory.get(type), valueRef);
  }

  public void addTransientValue(final String name, final MetaClass type, final Statement valueRef) {
    final TransientDataHolder holder = getOrCreateWritableDataHolder();

    Map<MetaClass, Statement> classStatementMap = holder.transientValues.get(name);
    if (classStatementMap == null) {
      holder.transientValues.put(name, classStatementMap = new HashMap<MetaClass, Statement>());
    }

    if (classStatementMap.containsKey(type)) {
      throw new RuntimeException("transient value already exists: " + name + "::" + type.getFullyQualifiedName());
    }

    final IOCProcessingContext pCtx = getInjectionContext().getProcessingContext();
    if (hasUnsatisfiedTransientValue(name, type)) {
      final Statement unsatisfiedTransientValue = getUnsatisfiedTransientValue(name, type);
      pCtx.append(Stmt.nestedCall(unsatisfiedTransientValue).invoke("set", valueRef));
      classStatementMap.put(type, Stmt.nestedCall(unsatisfiedTransientValue).invoke("get"));
      markSatisfied(name, type);
    }
    else {
      final String varName = InjectUtil.getUniqueVarName();
      pCtx.append(Stmt.declareFinalVariable(varName, type, valueRef));
      classStatementMap.put(type, Stmt.loadVariable(varName));
    }
  }

  public Statement getTransientValue(final String name, final Class type) {
    return getTransientValue(name, MetaClassFactory.get(type));
  }

  public Statement getTransientValue(final String name, final MetaClass type) {
    final TransientDataHolder holder = getTransientDataHolder();
    final Map<MetaClass, Statement> metaClassStatementMap = holder.transientValues.get(name);
    if (metaClassStatementMap != null) {
      final Statement statement = metaClassStatementMap.get(type);
      if (statement != null) {
        return statement;
      }
    }

    if (hasUnsatisfiedTransientValue(name, type)) {
      return Stmt.nestedCall(getUnsatisfiedTransientValue(name, type)).invoke("get");
    }

    final String holderVar = InjectUtil.getUniqueVarName();
    final MetaClass holderType = MetaClassFactory.parameterizedAs(RefHolder.class, MetaClassFactory.typeParametersOf(type));

    final IOCProcessingContext pCtx = getInjectionContext().getProcessingContext();
    pCtx.append(Stmt.declareFinalVariable(holderVar, holderType, Stmt.newObject(holderType)));

    addUnsatisifiedTransientValue(name, type, Stmt.loadVariable(holderVar));

    return Stmt.loadVariable(holderVar).invoke("get");
  }


  private void addUnsatisifiedTransientValue(final String name, final MetaClass type, final Statement holderRef) {
    final TransientDataHolder holder = getOrCreateWritableDataHolder();

    Map<MetaClass, Statement> metaClassStringMap = holder.unsatisfiedTransients.get(name);
    if (metaClassStringMap == null) {
      holder.unsatisfiedTransients.put(name, metaClassStringMap = new HashMap<MetaClass, Statement>());
    }

    metaClassStringMap.put(type, holderRef);
  }


  private Statement getUnsatisfiedTransientValue(final String name, final MetaClass type) {
    final TransientDataHolder holder = getTransientDataHolder();

    if (holder.unsatisfiedTransients.containsKey(name) && holder.unsatisfiedTransients.get(name).containsKey(type)) {
      return holder.unsatisfiedTransients.get(name).get(type);
    }
    return null;
  }

  private void markSatisfied(final String name, final MetaClass type) {
    final TransientDataHolder holder = getTransientDataHolder();

    if (holder.unsatisfiedTransients.containsKey(name) && holder.unsatisfiedTransients.get(name).containsKey(type)) {
      final Map<MetaClass, Statement> metaClassStatementMap = holder.unsatisfiedTransients.get(name);
      metaClassStatementMap.remove(type);
      if (metaClassStatementMap.isEmpty()) {
        holder.unsatisfiedTransients.remove(name);
      }
    }
  }

  public boolean hasAnyUnsatified() {
    return !getTransientDataHolder().unsatisfiedTransients.isEmpty();
  }

  public boolean hasUnsatisfiedTransientValue(final String name, final MetaClass type) {
    return getUnsatisfiedTransientValue(name, type) != null;
  }

  /**
   * Returns an instance of a {@link Statement} which represents the value associated for injection at this
   * InjectionPoint. This statement may represent a raw field access, a method call to a getter method, or an
   * internalized variable in the bootstrapper which is holding the value.
   *
   * @return a statement representing the value of the injection point.
   */
  public Statement getValueStatement() {

    final Statement[] stmt;
    final Statement val;
    final Injector inj;

    if (getTargetInjector().getInjectedType().equals(getEnclosingType()) &&
        // @Any is only implicitly added to injection SOURCES, so we must filter it out to do an exact comparison
        getTargetInjector().getQualifyingMetadata().filter(BuiltInQualifiers.ANY_INSTANCE).equals(getQualifyingMetadata()) &&
        getInjector() != null) {
      inj = getInjector();
    }
    else {
      inj = getTargetInjector();
    }
    if (getInjector() == null && inj.isDependent() && inj.isRegularTypeInjector()) {
      val = inj.getBeanInstance(this);
    } else {
      val = Refs.get(inj.getInstanceVarName());
    }

    switch (taskType) {
      case Field:
      case PrivateField:
        return InjectUtil.getPublicOrPrivateFieldValue(injectionContext,
            val,
            field);

      case PrivateMethod:
      case Method:
        if (method.getReturnType().isVoid()) {
          return Stmt.load(Void.class);
        }

        if (injectionContext.isAsync()) {
          stmt = AsyncInjectUtil.resolveInjectionDependencies(method.getParameters(), injectionContext, method);
        }
        else {
          stmt = InjectUtil.resolveInjectionDependencies(method.getParameters(), injectionContext, method);
        }


        return InjectUtil.invokePublicOrPrivateMethod(injectionContext,
            val,
            method,
            stmt);

      case Parameter:
        final Statement inlineStmt = injectionContext.getInlineBeanReference(parm);
        if (inlineStmt == null) {
          return Stmt.loadVariable("context").invoke("getBeanInstance",
              parm.getType(),
              InjectUtil.getQualifiersFromAnnotationsAsArray(parm.getAnnotations()));
        }
        else {
          return inlineStmt;
        }
      case Type:
        return val;

      default:
        return LiteralFactory.getLiteral(null);
    }
  }

  public Injector getTargetInjector() {
    final MetaClass targetType = getInjector() == null ? getEnclosingType() : getInjector().getInjectedType();

    switch(taskType) {
    case PrivateMethod:
    case Method:
    case Field:
    case PrivateField:
      // injectors for method and field producers are still registered by their enclosing class
      // so we must be sure to match against the qualifiers for that class
      try {
        // isProxy() does not compare against the right qualifiers, so instead
        // just try to return a proxied injector and see if that works
        return injectionContext.getProxiedInjector(targetType,
                JSR330QualifyingMetadata.createFromAnnotations(targetType.getAnnotations()));
      }
      catch (InjectionFailure ex) {
        return injectionContext.getInjector(targetType);
      }
    default:
      if (isProxy()) {
        return injectionContext.getProxiedInjector(targetType, getQualifyingMetadata());
      }
      else {
        return injectionContext.getQualifiedInjector(targetType, getQualifyingMetadata());
      }
    }
  }


  public Statement callOrBind(final Statement... values) {
    final Injector targetInjector = injector;

    MetaMethod meth = method;
    switch (taskType) {
      case PrivateField:
      case Field:
        return InjectUtil.setPublicOrPrivateFieldValue(
            injectionContext,
            Refs.get(targetInjector.getInstanceVarName()),
            field,
            values[0]);

      case Parameter:
        if (parm.getDeclaringMember() instanceof MetaMethod) {
          meth = (MetaMethod) parm.getDeclaringMember();
        }
        else {
          throw new RuntimeException("cannot call task on element: " + parm.getDeclaringMember());
        }

      case Method:
      case PrivateMethod:
        return InjectUtil.invokePublicOrPrivateMethod(injectionContext,
            Refs.get(targetInjector.getInstanceVarName()),
            meth,
            values);

      case Type:
      default:
        throw new RuntimeException("cannot call tasktype: " + taskType);
    }
  }


}
TOP

Related Classes of org.jboss.errai.ioc.rebind.ioc.injector.api.InjectableInstance$TransientDataHolder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.