All Downloads are FREE. Search and download functionalities are using the official Maven repository.

swim.runtime.agent.AgentClass Maven / Gradle / Ivy

// Copyright 2015-2019 SWIM.AI 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 swim.runtime.agent;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import swim.api.SwimLane;
import swim.api.SwimResident;
import swim.api.SwimTransient;
import swim.api.agent.AbstractAgentType;
import swim.api.agent.Agent;
import swim.api.agent.AgentContext;
import swim.api.agent.AgentException;
import swim.api.lane.CommandLane;
import swim.api.lane.DemandLane;
import swim.api.lane.DemandMapLane;
import swim.api.lane.JoinMapLane;
import swim.api.lane.JoinValueLane;
import swim.api.lane.Lane;
import swim.api.lane.ListLane;
import swim.api.lane.MapLane;
import swim.api.lane.SpatialLane;
import swim.api.lane.SupplyLane;
import swim.api.lane.ValueLane;
import swim.collections.HashTrieMap;
import swim.runtime.lane.CommandLaneView;
import swim.runtime.lane.DemandLaneView;
import swim.runtime.lane.DemandMapLaneView;
import swim.runtime.lane.JoinMapLaneView;
import swim.runtime.lane.JoinValueLaneView;
import swim.runtime.lane.ListLaneView;
import swim.runtime.lane.MapLaneView;
import swim.runtime.lane.SpatialLaneView;
import swim.runtime.lane.SupplyLaneView;
import swim.runtime.lane.ValueLaneView;
import swim.structure.Form;
import swim.structure.Record;
import swim.structure.Value;
import swim.uri.Uri;

public abstract class AgentClass extends AbstractAgentType {
  final Class agentType;

  final Constructor constructor;

  AgentClass(Class agentType, Constructor constructor) {
    this.agentType = agentType;
    this.constructor = constructor;
    constructor.setAccessible(true);
  }

  @Override
  public Class type() {
    return this.agentType;
  }

  @Override
  public Value props(Uri nodeUri) {
    final HashTrieMap params = route().unapply(nodeUri);
    final Record props = Record.of();
    for (HashTrieMap.Entry param : params) {
      props.slot(param.getKey(), param.getValue());
    }
    return props;
  }

  @Override
  public abstract A createAgent(AgentContext agentContext);

  public static  AgentClass apply(Class agentType) {
    try {
      return new ContextConstructor(agentType, agentType.getDeclaredConstructor(AgentContext.class));
    } catch (NoSuchMethodException e) {
      try {
        return new NoArgConstructor(agentType, agentType.getDeclaredConstructor());
      } catch (NoSuchMethodException cause) {
        throw new AgentException(cause);
      }
    }
  }

  static void reflectLaneFields(Class agentType, AgentContext agentContext, Agent agent) {
    if (agentType != null) {
      reflectLaneFields(agentType.getSuperclass(), agentContext, agent);
      final Field[] fields = agentType.getDeclaredFields();
      for (Field field : fields) {
        if (Lane.class.isAssignableFrom(field.getType())) {
          field.setAccessible(true);
          reflectLaneField(field, agentContext, agent);
        }
      }
    }
  }

  static void reflectLaneField(Field field, AgentContext agentContext, Agent agent) {
    final SwimLane swimLane = field.getAnnotation(SwimLane.class);
    if (swimLane != null) {
      final Lane lane = reflectLaneType(agent, field, field.getGenericType());
      final Uri laneUri = Uri.parse(swimLane.value());
      agentContext.openLane(laneUri, lane);
    }
  }

  static Lane reflectLaneType(Agent agent, Field field, Type type) {
    if (type instanceof ParameterizedType) {
      return reflectParameterizedLaneType(agent, field, (ParameterizedType) type);
    }
    return reflectOtherLaneType(agent, field, type);
  }

  static Lane reflectParameterizedLaneType(Agent agent, Field field, ParameterizedType type) {
    final Type rawType = type.getRawType();
    if (rawType instanceof Class) {
      return reflectLaneTypeArguments(agent, field, (Class) rawType, type.getActualTypeArguments());
    }
    return reflectOtherLaneType(agent, field, type);
  }

  static Lane reflectLaneTypeArguments(Agent agent, Field field, Class type, Type[] arguments) {
    if (CommandLane.class.equals(type)) {
      return reflectCommandLaneType(agent, field, type, arguments);
    } else if (DemandLane.class.equals(type)) {
      return reflectDemandLaneType(agent, field, type, arguments);
    } else if (DemandMapLane.class.equals(type)) {
      return reflectDemandMapLaneType(agent, field, type, arguments);
    } else if (JoinMapLane.class.equals(type)) {
      return reflectJoinMapLaneType(agent, field, type, arguments);
    } else if (JoinValueLane.class.equals(type)) {
      return reflectJoinValueLaneType(agent, field, type, arguments);
    } else if (ListLane.class.equals(type)) {
      return reflectListLaneType(agent, field, type, arguments);
    } else if (MapLane.class.equals(type)) {
      return reflectMapLaneType(agent, field, type, arguments);
    } else if (SpatialLane.class.equals(type)) {
      return reflectSpatialLaneType(agent, field, type, arguments);
    } else if (SupplyLane.class.equals(type)) {
      return reflectSupplyLaneType(agent, field, type, arguments);
    } else if (ValueLane.class.equals(type)) {
      return reflectValueLaneType(agent, field, type, arguments);
    }
    return reflectOtherLaneType(agent, field, type);
  }

  @SuppressWarnings("unchecked")
  static Lane reflectCommandLaneType(Agent agent, Field field, Class type, Type[] arguments) {
    try {
      Object object = field.get(agent);
      if (object == null) {
        object = agent.agentContext().commandLane();
        field.set(agent, object);
      }
      if (object instanceof CommandLaneView) {
        final CommandLaneView lane = (CommandLaneView) object;
        Form valueForm = lane.valueForm();
        final Type valueType = arguments[0];
        if (valueForm == null && valueType instanceof Class) {
          valueForm = Form.forClass((Class) valueType);
          lane.setValueForm(valueForm);
        }
        return lane;
      }
      return reflectOtherLaneType(agent, field, type);
    } catch (IllegalAccessException cause) {
      throw new AgentException(cause);
    }
  }

  @SuppressWarnings("unchecked")
  static Lane reflectDemandLaneType(Agent agent, Field field, Class type, Type[] arguments) {
    try {
      Object object = field.get(agent);
      if (object == null) {
        object = agent.agentContext().demandLane();
        field.set(agent, object);
      }
      if (object instanceof DemandLaneView) {
        final DemandLaneView lane = (DemandLaneView) object;
        Form valueForm = lane.valueForm();
        final Type valueType = arguments[0];
        if (valueForm == null && valueType instanceof Class) {
          valueForm = Form.forClass((Class) valueType);
          lane.setValueForm(valueForm);
        }
        return lane;
      }
      return reflectOtherLaneType(agent, field, type);
    } catch (IllegalAccessException cause) {
      throw new AgentException(cause);
    }
  }

  @SuppressWarnings("unchecked")
  static Lane reflectDemandMapLaneType(Agent agent, Field field, Class type, Type[] arguments) {
    try {
      Object object = field.get(agent);
      if (object == null) {
        object = agent.agentContext().demandMapLane();
        field.set(agent, object);
      }
      if (object instanceof DemandMapLaneView) {
        final DemandMapLaneView lane = (DemandMapLaneView) object;
        Form keyForm = lane.keyForm();
        final Type keyType = arguments[0];
        if (keyForm == null && keyType instanceof Class) {
          keyForm = Form.forClass((Class) keyType);
          lane.setKeyForm(keyForm);
        }
        Form valueForm = lane.valueForm();
        final Type valueType = arguments[1];
        if (valueForm == null && valueType instanceof Class) {
          valueForm = Form.forClass((Class) valueType);
          lane.setValueForm(valueForm);
        }
        return lane;
      }
      return reflectOtherLaneType(agent, field, type);
    } catch (IllegalAccessException cause) {
      throw new AgentException(cause);
    }
  }

  @SuppressWarnings("unchecked")
  static Lane reflectJoinMapLaneType(Agent agent, Field field, Class type, Type[] arguments) {
    try {
      Object object = field.get(agent);
      if (object == null) {
        object = agent.agentContext().joinMapLane();
        field.set(agent, object);
      }
      if (object instanceof JoinMapLaneView) {
        final JoinMapLaneView lane = (JoinMapLaneView) object;
        Form linkForm = lane.linkForm();
        final Type linkType = arguments[0];
        if (linkForm == null && linkType instanceof Class) {
          linkForm = Form.forClass((Class) linkType);
          lane.setLinkForm(linkForm);
        }
        Form keyForm = lane.keyForm();
        final Type keyType = arguments[1];
        if (keyForm == null && keyType instanceof Class) {
          keyForm = Form.forClass((Class) keyType);
          lane.setKeyForm(keyForm);
        }
        Form valueForm = lane.valueForm();
        final Type valueType = arguments[2];
        if (valueForm == null && valueType instanceof Class) {
          valueForm = Form.forClass((Class) valueType);
          lane.setValueForm(valueForm);
        }
        final SwimResident swimResident = field.getAnnotation(SwimResident.class);
        if (swimResident != null) {
          lane.isResident(swimResident.value());
        }
        final SwimTransient swimTransient = field.getAnnotation(SwimTransient.class);
        if (swimTransient != null) {
          lane.isTransient(swimTransient.value());
        }
        return lane;
      }
      return reflectOtherLaneType(agent, field, type);
    } catch (IllegalAccessException cause) {
      throw new AgentException(cause);
    }
  }

  @SuppressWarnings("unchecked")
  static Lane reflectJoinValueLaneType(Agent agent, Field field, Class type, Type[] arguments) {
    try {
      Object object = field.get(agent);
      if (object == null) {
        object = agent.agentContext().joinValueLane();
        field.set(agent, object);
      }
      if (object instanceof JoinValueLaneView) {
        final JoinValueLaneView lane = (JoinValueLaneView) object;
        Form keyForm = lane.keyForm();
        final Type keyType = arguments[0];
        if (keyForm == null && keyType instanceof Class) {
          keyForm = Form.forClass((Class) keyType);
          lane.setKeyForm(keyForm);
        }
        Form valueForm = lane.valueForm();
        final Type valueType = arguments[1];
        if (valueForm == null && valueType instanceof Class) {
          valueForm = Form.forClass((Class) valueType);
          lane.setValueForm(valueForm);
        }
        final SwimResident swimResident = field.getAnnotation(SwimResident.class);
        if (swimResident != null) {
          lane.isResident(swimResident.value());
        }
        final SwimTransient swimTransient = field.getAnnotation(SwimTransient.class);
        if (swimTransient != null) {
          lane.isTransient(swimTransient.value());
        }
        return lane;
      }
      return reflectOtherLaneType(agent, field, type);
    } catch (IllegalAccessException cause) {
      throw new AgentException(cause);
    }
  }

  @SuppressWarnings("unchecked")
  static Lane reflectListLaneType(Agent agent, Field field, Class type, Type[] arguments) {
    try {
      Object object = field.get(agent);
      if (object == null) {
        object = agent.agentContext().listLane();
        field.set(agent, object);
      }
      if (object instanceof ListLaneView) {
        final ListLaneView lane = (ListLaneView) object;
        Form valueForm = lane.valueForm();
        final Type valueType = arguments[0];
        if (valueForm == null && valueType instanceof Class) {
          valueForm = Form.forClass((Class) valueType);
          lane.setValueForm(valueForm);
        }
        final SwimResident swimResident = field.getAnnotation(SwimResident.class);
        if (swimResident != null) {
          lane.isResident(swimResident.value());
        }
        final SwimTransient swimTransient = field.getAnnotation(SwimTransient.class);
        if (swimTransient != null) {
          lane.isTransient(swimTransient.value());
        }
        return lane;
      }
      return reflectOtherLaneType(agent, field, type);
    } catch (IllegalAccessException cause) {
      throw new AgentException(cause);
    }
  }

  @SuppressWarnings("unchecked")
  static Lane reflectMapLaneType(Agent agent, Field field, Class type, Type[] arguments) {
    try {
      Object object = field.get(agent);
      if (object == null) {
        object = agent.agentContext().mapLane();
        field.set(agent, object);
      }
      if (object instanceof MapLaneView) {
        final MapLaneView lane = (MapLaneView) object;
        Form keyForm = lane.keyForm();
        final Type keyType = arguments[0];
        if (keyForm == null && keyType instanceof Class) {
          keyForm = Form.forClass((Class) keyType);
          lane.setKeyForm(keyForm);
        }
        Form valueForm = lane.valueForm();
        final Type valueType = arguments[1];
        if (valueForm == null && valueType instanceof Class) {
          valueForm = Form.forClass((Class) valueType);
          lane.setValueForm(valueForm);
        }
        final SwimResident swimResident = field.getAnnotation(SwimResident.class);
        if (swimResident != null) {
          lane.isResident(swimResident.value());
        }
        final SwimTransient swimTransient = field.getAnnotation(SwimTransient.class);
        if (swimTransient != null) {
          lane.isTransient(swimTransient.value());
        }
        return lane;
      }
      return reflectOtherLaneType(agent, field, type);
    } catch (IllegalAccessException cause) {
      throw new AgentException(cause);
    }
  }

  @SuppressWarnings("unchecked")
  static Lane reflectSpatialLaneType(Agent agent, Field field, Class type, Type[] arguments) {
    try {
      Object object = field.get(agent);
      if (object == null) {
        object = agent.agentContext().geospatialLane();
        field.set(agent, object);
      }
      if (object instanceof SpatialLaneView) {
        final SpatialLaneView lane = (SpatialLaneView) object;
        Form keyForm = lane.keyForm();
        final Type keyType = arguments[0];
        if (keyForm == null && keyType instanceof Class) {
          keyForm = Form.forClass((Class) keyType);
          lane.setKeyForm(keyForm);
        }
        Form valueForm = lane.valueForm();
        final Type valueType = arguments[2];
        if (valueForm == null && valueType instanceof Class) {
          valueForm = Form.forClass((Class) valueType);
          lane.setValueForm(valueForm);
        }
        final SwimResident swimResident = field.getAnnotation(SwimResident.class);
        if (swimResident != null) {
          lane.isResident(swimResident.value());
        }
        final SwimTransient swimTransient = field.getAnnotation(SwimTransient.class);
        if (swimTransient != null) {
          lane.isTransient(swimTransient.value());
        }
        return lane;
      }
      return reflectOtherLaneType(agent, field, type);
    } catch (IllegalAccessException cause) {
      throw new AgentException(cause);
    }
  }

  @SuppressWarnings("unchecked")
  static Lane reflectSupplyLaneType(Agent agent, Field field, Class type, Type[] arguments) {
    try {
      Object object = field.get(agent);
      if (object == null) {
        object = agent.agentContext().supplyLane();
        field.set(agent, object);
      }
      if (object instanceof SupplyLaneView) {
        final SupplyLaneView lane = (SupplyLaneView) object;
        Form valueForm = lane.valueForm();
        final Type valueType = arguments[0];
        if (valueForm == null && valueType instanceof Class) {
          valueForm = Form.forClass((Class) valueType);
          lane.setValueForm(valueForm);
        }
        return lane;
      }
      return reflectOtherLaneType(agent, field, type);
    } catch (IllegalAccessException cause) {
      throw new AgentException(cause);
    }
  }

  @SuppressWarnings("unchecked")
  static Lane reflectValueLaneType(Agent agent, Field field, Class type, Type[] arguments) {
    try {
      Object object = field.get(agent);
      if (object == null) {
        object = agent.agentContext().valueLane();
        field.set(agent, object);
      }
      if (object instanceof ValueLaneView) {
        final ValueLaneView lane = (ValueLaneView) object;
        Form valueForm = lane.valueForm();
        final Type valueType = arguments[0];
        if (valueForm == null && valueType instanceof Class) {
          valueForm = Form.forClass((Class) valueType);
          lane.setValueForm(valueForm);
        }
        final SwimResident swimResident = field.getAnnotation(SwimResident.class);
        if (swimResident != null) {
          lane.isResident(swimResident.value());
        }
        final SwimTransient swimTransient = field.getAnnotation(SwimTransient.class);
        if (swimTransient != null) {
          lane.isTransient(swimTransient.value());
        }
        return lane;
      }
      return reflectOtherLaneType(agent, field, type);
    } catch (IllegalAccessException cause) {
      throw new AgentException(cause);
    }
  }

  static Lane reflectOtherLaneType(Agent agent, Field field, Type type) {
    try {
      final Object object = field.get(agent);
      if (object instanceof Lane) {
        return (Lane) object;
      } else {
        throw new AgentException("unknown lane type: " + type);
      }
    } catch (IllegalAccessException cause) {
      throw new AgentException(cause);
    }
  }

  static final class ContextConstructor extends AgentClass {
    ContextConstructor(Class agentType, Constructor constructor) {
      super(agentType, constructor);
    }

    @Override
    public A createAgent(AgentContext agentContext) {
      try {
        final A agent = constructor.newInstance(agentContext);
        reflectLaneFields(agentType, agentContext, agent);
        return agent;
      } catch (ReflectiveOperationException cause) {
        throw new AgentException(cause);
      }
    }
  }

  static final class NoArgConstructor extends AgentClass {
    NoArgConstructor(Class agentType, Constructor constructor) {
      super(agentType, constructor);
    }

    @Override
    public A createAgent(AgentContext agentContext) {
      try {
        final A agent = constructor.newInstance();
        reflectLaneFields(agentType, agentContext, agent);
        return agent;
      } catch (ReflectiveOperationException cause) {
        throw new AgentException(cause);
      }
    }
  }
}