/** {@inheritDoc} */
  @Override
  public ActionForward execute(
      @SuppressWarnings("unused") ActionMapping mapping,
      @SuppressWarnings("unused") ActionForm form,
      HttpServletRequest request,
      HttpServletResponse response)
      throws Exception {
    HttpSession session = request.getSession();
    final InterMineAPI im = SessionMethods.getInterMineAPI(session);
    ObjectStore os = im.getObjectStore();
    WebConfig webConfig = SessionMethods.getWebConfig(request);
    Integer objectId = new Integer(request.getParameter("object"));
    String fieldName = request.getParameter("field");
    String fileType = request.getParameter("type");
    InterMineObject object = os.getObjectById(objectId);

    FieldExporter fieldExporter = null;

    Set classes = DynamicUtil.decomposeClass(object.getClass());

    Iterator classIter = classes.iterator();

    while (classIter.hasNext()) {
      Class c = (Class) classIter.next();

      Type thisTypeConfig = webConfig.getTypes().get(c.getName());

      FieldConfig fc = thisTypeConfig.getFieldConfigMap().get(fieldName);

      if (fc != null) {
        String fieldExporterClassName = fc.getFieldExporter();
        if (fieldExporterClassName != null) {
          fieldExporter = (FieldExporter) Class.forName(fieldExporterClassName).newInstance();
          break;
        }
      }
    }

    if (fieldExporter == null) {
      Object fieldValue = object.getFieldValue(fieldName);
      if (fileType == null || fileType.length() == 0) {
        response.setContentType("text/plain; charset=UTF-8");
        response.setHeader("Content-Disposition ", "inline; filename=" + fieldName + ".txt");
      } else {
        response.setContentType("text/" + fileType);
        response.setHeader(
            "Content-Disposition ", "inline; filename=" + fieldName + "." + fileType);
      }
      PrintStream out = new PrintStream(response.getOutputStream());
      if (fieldValue instanceof ClobAccess) {
        ((ClobAccess) fieldValue).drainToPrintStream(out);
      } else {
        out.print(fieldValue);
      }
      out.flush();
    } else {
      fieldExporter.exportField(object, fieldName, os, response);
    }
    return null;
  }
 // In a future Java8 world, this should produce a Stream<FieldConfig>, to avoid the double loop.
 private static Collection<FieldConfig> resultConfigs(WebConfig webConfig, ClassDescriptor cld) {
   final List<FieldConfig> fieldConfigs = getClassFieldConfigs(webConfig, cld);
   Iterator<FieldConfig> it = fieldConfigs.iterator();
   while (it.hasNext()) {
     FieldConfig f = it.next();
     if (!f.getShowInResults()) {
       it.remove();
     }
   }
   return fieldConfigs;
 }
  /**
   * Return a list of string paths that are defined as WebConfig to be shown in results. This will
   * include only attributes of the given class and not follow references. Optionally provide a
   * prefix to for creating a view for references/collections.
   *
   * @param type the class name to create a view for
   * @param model the model
   * @param webConfig we configuration
   * @param startingPath a path to prefix the class, can be null
   * @return the configured view paths for the class
   */
  public static List<String> getDefaultViewForClass(
      String type, Model model, WebConfig webConfig, String startingPath) {
    String prefix = startingPath;
    List<String> view = new ArrayList<String>();
    ClassDescriptor cld = model.getClassDescriptorByName(type);
    List<FieldConfig> fieldConfigs = getClassFieldConfigs(webConfig, cld);
    if (!StringUtils.isEmpty(prefix)) {
      try {
        // we can't add a subclass constraint, type must be same as the end of the prefix
        Path prefixPath = new Path(model, prefix);
        String prefixEndType = TypeUtil.unqualifiedName(prefixPath.getEndType().getName());
        if (!prefixEndType.equals(type)) {
          throw new IllegalArgumentException(
              "Mismatch between end type of prefix: "
                  + prefixEndType
                  + " and type parameter: "
                  + type);
        }
      } catch (PathException e) {
        LOG.error("Invalid path configured in webconfig for class: " + type);
      }
    } else {
      prefix = type;
    }

    for (FieldConfig fieldConfig : fieldConfigs) {
      String relPath = fieldConfig.getFieldExpr();
      // only add attributes, don't follow references, following references can be problematic
      // when subclasses get involved.
      if (fieldConfig.getShowInResults()) {
        try {
          Path path = new Path(model, prefix + "." + relPath);
          // use type (e.g. Protein) not prefix (e.g. Gene.proteins) to do
          // attribute check
          Path checkIsOnlyAttribute = new Path(model, type + "." + relPath);
          if (checkIsOnlyAttribute.isOnlyAttribute()) {
            view.add(path.getNoConstraintsString());
          }
        } catch (PathException e) {
          LOG.error("Invalid path configured in webconfig for class: " + type);
        }
      }
    }
    if (view.size() == 0) {
      for (AttributeDescriptor att : cld.getAllAttributeDescriptors()) {
        if (!"id".equals(att.getName())) {
          view.add(prefix + "." + att.getName());
        }
      }
    }
    return view;
  }
 private static List<String> getSubview(WebConfig webConfig, Model m, Path path)
     throws PathException {
   List<String> subview = new ArrayList<String>();
   String basePath = path.toStringNoConstraints() + ".";
   List<FieldConfig> subconfs = getClassFieldConfigs(webConfig, path.getEndClassDescriptor());
   for (FieldConfig fc : subconfs) {
     String pathString = basePath + fc.getFieldExpr();
     Path pathToAdd = new Path(m, pathString);
     if (pathToAdd.endIsAttribute() && (fc.getDisplayer() == null && fc.getShowInSummary())) {
       subview.add(pathToAdd.getNoConstraintsString());
     }
   }
   return subview;
 }
 private Map<String, Object> getObjectDetails(InterMineObject imo) {
   WebConfig webConfig = InterMineContext.getWebConfig();
   Model m = im.getModel();
   Map<String, Object> objectDetails = new HashMap<String, Object>();
   String className = DynamicUtil.getSimpleClassName(imo.getClass());
   ClassDescriptor cd = m.getClassDescriptorByName(className);
   for (FieldConfig fc : FieldConfigHelper.getClassFieldConfigs(webConfig, cd)) {
     try {
       Path p = new Path(m, cd.getUnqualifiedName() + "." + fc.getFieldExpr());
       if (p.endIsAttribute() && fc.getShowInSummary()) {
         objectDetails.put(
             p.getNoConstraintsString().replaceAll("^[^.]*\\.", ""), PathUtil.resolvePath(p, imo));
       }
     } catch (PathException e) {
       LOG.error(e);
     }
   }
   return objectDetails;
 }
  /**
   * Used for making a query for a reference or collection. Only used when a user clicks on [show
   * all] under an inline table on an Object's report page. The type of that object is
   * "startingPath", eg. Department. This path will be prepended to every path in the query. The
   * "type" is the type of the reference/collection, eg. Employee.
   *
   * <p>TODO use getDefaultViewForClass() instead
   *
   * @param objType class of object we are querying for eg. Manager
   * @param model the model
   * @param webConfig the webconfig
   * @param fieldType the type of the field this object is in, eg Employee
   * @return query, eg. Department.employees.name
   */
  protected static PathQuery getQueryWithDefaultView(
      String objType, Model model, WebConfig webConfig, String fieldType) {
    String prefix = fieldType;
    PathQuery query = new PathQuery(model);
    ClassDescriptor cld = model.getClassDescriptorByName(objType);
    List<FieldConfig> fieldConfigs = getClassFieldConfigs(webConfig, cld);

    if (!StringUtils.isBlank(prefix)) {
      try {
        // if the type is different to the end of the prefix path, add a subclass constraint
        Path fieldPath = new Path(model, fieldType);
        String fieldEndType = TypeUtil.unqualifiedName(fieldPath.getEndType().getName());
        if (!fieldEndType.equals(objType)) {
          query.addConstraint(Constraints.type(fieldType, objType));
        }
      } catch (PathException e) {
        LOG.error("Invalid path configured in webconfig for class: " + objType);
      }
    }

    for (FieldConfig fieldConfig : fieldConfigs) {
      if (fieldConfig.getShowInResults()) {
        String path = prefix + "." + fieldConfig.getFieldExpr();
        int from = prefix.length() + 1;
        while (path.indexOf('.', from) != -1) {
          int dotPos = path.indexOf('.', from);
          int nextDot = path.indexOf('.', dotPos + 1);
          String outerJoin = nextDot == -1 ? path.substring(0, dotPos) : path.substring(0, nextDot);
          query.setOuterJoinStatus(outerJoin, OuterJoinStatus.OUTER);
          from = dotPos + 1;
        }
        query.addView(path);
      }
    }
    if (query.getView().size() == 0) {
      for (AttributeDescriptor att : cld.getAllAttributeDescriptors()) {
        if (!"id".equals(att.getName())) {
          query.addView(prefix + "." + att.getName());
        }
      }
    }
    return query;
  }
  /** {@inheritDoc} */
  @Override
  public ActionForward execute(
      ComponentContext context,
      ActionMapping mapping,
      ActionForm form,
      HttpServletRequest request,
      HttpServletResponse response)
      throws Exception {
    HttpSession session = request.getSession();
    final InterMineAPI im = SessionMethods.getInterMineAPI(session);

    InterMineBag imBag = (InterMineBag) request.getAttribute("bag");
    WebConfig webConfig = SessionMethods.getWebConfig(request);
    Model model = im.getModel();
    TemplateManager templateManager = im.getTemplateManager();

    Map<Class, ApiTemplate> conversionTypesMap =
        TypeConverter.getConversionTemplates(
            templateManager.getConversionTemplates(),
            TypeUtil.instantiate(model.getPackageName() + "." + imBag.getType()));
    ArrayList<String> conversionTypes = new ArrayList<String>();
    Map<Type, Boolean> fastaMap = new HashMap<Type, Boolean>();
    for (Class<?> clazz : conversionTypesMap.keySet()) {
      conversionTypes.add(TypeUtil.unqualifiedName(clazz.getName()));
      Type type = webConfig.getTypes().get(clazz.getName());
      FieldConfig fieldConfig = type.getFieldConfigMap().get("length");
      if (fieldConfig != null && fieldConfig.getDisplayer() != null) {
        fastaMap.put(type, Boolean.TRUE);
      } else {
        fastaMap.put(type, Boolean.FALSE);
      }
    }
    // Use custom converters
    BagQueryConfig bagQueryConfig = im.getBagQueryConfig();
    String bagType = imBag.getType();
    Set<AdditionalConverter> additionalConverters = bagQueryConfig.getAdditionalConverters(bagType);
    request.setAttribute("customConverters", additionalConverters);
    request.setAttribute("conversionTypes", conversionTypes);
    request.setAttribute("fastaMap", fastaMap);
    return null;
  }
 /**
  * Get the view as configured in the webconfig. Guarantees to return a non-empty non-null list.
  *
  * @param type The type we are trying to get a view for.
  * @param model The data model
  * @param webConfig The web-configuration.
  * @param fieldConfigs
  * @return The list of paths that we can use to construct a query.
  * @throws UnconfiguredException if the class has not configured view.
  */
 private static List<String> getConfiguredView(ClassDescriptor cld, WebConfig webConfig)
     throws UnconfiguredException {
   Collection<String> view = new LinkedHashSet<String>(); // Preserve order and uniqueness.
   Model m = cld.getModel();
   for (FieldConfig fieldConfig : resultConfigs(webConfig, cld)) {
     try {
       Path p = new Path(m, cld.getUnqualifiedName() + "." + fieldConfig.getFieldExpr());
       // add subpaths of references and roots, attrs themselves, ignore collections.
       if (p.isRootPath() || p.endIsReference()) {
         view.addAll(getSubview(webConfig, m, p));
       } else if (p.endIsAttribute()) {
         view.add(p.getNoConstraintsString());
       }
     } catch (PathException e) {
       LOG.error("Invalid path configured in webconfig for class: " + cld);
     }
   }
   if (view.isEmpty()) {
     throw new UnconfiguredException();
   }
   return new ArrayList<String>(view);
 }
  /**
   * Return a list of string paths that are defined as WebConfig to be shown in results. This will
   * include attributes of the given class and follow references.
   *
   * @param type the class name to create a view for
   * @param model the model
   * @param webConfig we configuration
   * @return the configured view paths for the class
   */
  public static List<String> getDefaultViewForClass(String type, Model model, WebConfig webConfig) {
    List<String> view = new ArrayList<String>();
    ClassDescriptor cld = model.getClassDescriptorByName(type);
    List<FieldConfig> fieldConfigs = FieldConfigHelper.getClassFieldConfigs(webConfig, cld);

    for (FieldConfig fieldConfig : fieldConfigs) {
      String relPath = fieldConfig.getFieldExpr();
      // only add attributes, don't follow references, following references can be problematic
      // when subclasses get involved.
      if (fieldConfig.getShowInResults()) {
        try {
          Path path = new Path(model, type + "." + relPath);
          // add references
          if (path.isRootPath() || path.endIsReference()) {
            for (FieldConfig fc :
                FieldConfigHelper.getClassFieldConfigs(webConfig, path.getEndClassDescriptor())) {
              Path pathToAdd =
                  new Path(model, path.toStringNoConstraints() + "." + fc.getFieldExpr());
              if (pathToAdd.endIsAttribute()
                  && (!view.contains(pathToAdd.getNoConstraintsString()))
                  && (fc.getDisplayer() == null && fc.getShowInSummary())) {
                view.add(pathToAdd.getNoConstraintsString());
              }
            }
            // add collections
          } else if (!path.endIsCollection()) {
            view.add(path.getNoConstraintsString());
          }
        } catch (PathException e) {
          LOG.error("Invalid path configured in webconfig for class: " + type);
        }
      }
    }
    if (view.size() == 0) {
      for (AttributeDescriptor att : cld.getAllAttributeDescriptors()) {
        if (!"id".equals(att.getName())) {
          view.add(type + "." + att.getName());
        }
      }
    }
    return view;
  }