/**
   * Creates a new parameters panel with a list of input fields matching the {@link Param}s for the
   * factory related to the {@code DataStoreInfo} that's the model of the provided {@code Form}.
   *
   * @param componentId the id for this component instance
   * @param storeEditForm the form being build by the calling class, whose model is the {@link
   *     DataStoreInfo} being edited
   */
  public DefaultDataStoreEditPanel(final String componentId, final Form storeEditForm) {
    super(componentId, storeEditForm);

    final IModel model = storeEditForm.getModel();
    final DataStoreInfo info = (DataStoreInfo) model.getObject();
    final Catalog catalog = getCatalog();
    final ResourcePool resourcePool = catalog.getResourcePool();
    DataAccessFactory dsFactory;
    try {
      dsFactory = resourcePool.getDataStoreFactory(info);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }

    final Map<String, ParamInfo> paramsMetadata = new LinkedHashMap<String, ParamInfo>();

    {
      final boolean isNew = null == info.getId();
      final Param[] dsParams = dsFactory.getParametersInfo();
      for (Param p : dsParams) {
        ParamInfo paramInfo = new ParamInfo(p);
        // hide the repository params, the resource pool will inject it transparently
        if (!Repository.class.equals(paramInfo.getBinding())) {
          paramsMetadata.put(p.key, paramInfo);
          if (isNew && !p.isDeprecated()) {
            applyParamDefault(paramInfo, info);
          }
        }
      }
    }

    final List<String> keys = new ArrayList<String>(paramsMetadata.keySet());
    final IModel paramsModel = new PropertyModel(model, "connectionParameters");

    ListView paramsList =
        new ListView("parameters", keys) {
          private static final long serialVersionUID = 1L;

          @Override
          protected void populateItem(ListItem item) {
            String paramName = item.getDefaultModelObjectAsString();
            ParamInfo paramMetadata = paramsMetadata.get(paramName);

            Component inputComponent;
            inputComponent = getInputComponent("parameterPanel", paramsModel, paramMetadata);

            String description = paramMetadata.getTitle();
            if (description != null) {
              inputComponent.add(new SimpleAttributeModifier("title", description));
            }
            item.add(inputComponent);
          }
        };
    // needed for form components not to loose state
    paramsList.setReuseItems(true);

    add(paramsList);
  }
  @Override
  protected String handleObjectPost(Object object) throws Exception {
    String workspace = getAttribute("workspace");

    DataStoreInfo ds = (DataStoreInfo) object;
    if (ds.getWorkspace() != null) {
      // ensure the specifried workspace matches the one dictated by the uri
      WorkspaceInfo ws = (WorkspaceInfo) ds.getWorkspace();
      if (!workspace.equals(ws.getName())) {
        throw new RestletException(
            "Expected workspace " + workspace + " but client specified " + ws.getName(),
            Status.CLIENT_ERROR_FORBIDDEN);
      }
    } else {
      ds.setWorkspace(catalog.getWorkspaceByName(workspace));
    }
    ds.setEnabled(true);

    // if no namespace parameter set, set it
    // TODO: we should really move this sort of thing to be something central
    if (!ds.getConnectionParameters().containsKey("namespace")) {
      WorkspaceInfo ws = ds.getWorkspace();
      NamespaceInfo ns = catalog.getNamespaceByPrefix(ws.getName());
      if (ns == null) {
        ns = catalog.getDefaultNamespace();
      }
      if (ns != null) {
        ds.getConnectionParameters().put("namespace", ns.getURI());
      }
    }

    // attempt to set the datastore type
    try {
      DataAccessFactory factory = DataStoreUtils.aquireFactory(ds.getConnectionParameters());
      ds.setType(factory.getDisplayName());
    } catch (Exception e) {
      LOGGER.warning("Unable to determine datastore type from connection parameters");
      if (LOGGER.isLoggable(Level.FINE)) {
        LOGGER.log(Level.FINE, "", e);
      }
    }

    catalog.validate((DataStoreInfo) object, false).throwIfInvalid();
    catalog.add((DataStoreInfo) object);

    LOGGER.info("POST data store " + ds.getName());
    return ds.getName();
  }
  /**
   * Returns the appropriate icon for the specified store.
   *
   * @param storeInfo
   * @return
   * @see #getStoreIcon(Class)
   */
  public ResourceReference getStoreIcon(final StoreInfo storeInfo) {

    Class<?> factoryClass = null;

    Catalog catalog = storeInfo.getCatalog();
    final ResourcePool resourcePool = catalog.getResourcePool();

    if (storeInfo instanceof DataStoreInfo) {
      DataAccessFactory dataStoreFactory = null;
      try {
        dataStoreFactory = resourcePool.getDataStoreFactory((DataStoreInfo) storeInfo);
      } catch (IOException e) {
        LOGGER.log(
            Level.INFO, "factory class for storeInfo " + storeInfo.getName() + " not found", e);
      }

      if (dataStoreFactory != null) {
        return getStoreIcon(dataStoreFactory.getClass());
      }

    } else if (storeInfo instanceof CoverageStoreInfo) {
      AbstractGridFormat format = resourcePool.getGridCoverageFormat((CoverageStoreInfo) storeInfo);
      if (format != null) {
        return getStoreIcon(format.getClass());
      }
    } else if (storeInfo instanceof WMSStoreInfo) {
      return MAP_STORE_ICON;
    } else {
      throw new IllegalStateException(storeInfo.getClass().getName());
    }

    LOGGER.info(
        "Could not determine icon for StoreInfo "
            + storeInfo.getName()
            + ". Using 'unknown' icon.");
    return UNKNOWN_ICON;
  }
  /**
   * @param storeInfo
   * @param app
   * @return the extension point descriptor for the given storeInfo, or {@code null} if there's no
   *     contribution specific for the given storeInfo's type
   */
  private static DataStorePanelInfo findPanelInfo(
      final StoreInfo storeInfo, final GeoServerApplication app) {

    final Catalog catalog = storeInfo.getCatalog();
    final ResourcePool resourcePool = catalog.getResourcePool();

    Class<?> factoryClass = null;
    if (storeInfo instanceof DataStoreInfo) {
      DataAccessFactory storeFactory;
      try {
        storeFactory = resourcePool.getDataStoreFactory((DataStoreInfo) storeInfo);
      } catch (IOException e) {
        throw new IllegalArgumentException("no factory found for StoreInfo " + storeInfo);
      }
      if (storeFactory != null) {
        factoryClass = storeFactory.getClass();
      }
    } else if (storeInfo instanceof CoverageStoreInfo) {
      AbstractGridFormat gridFormat;
      gridFormat = resourcePool.getGridCoverageFormat((CoverageStoreInfo) storeInfo);
      if (gridFormat != null) {
        factoryClass = gridFormat.getClass();
      }
    } else {
      throw new IllegalArgumentException("Unknown store type: " + storeInfo.getClass().getName());
    }

    if (factoryClass == null) {
      throw new IllegalArgumentException("Can't locate the factory for the store");
    }

    final List<DataStorePanelInfo> providers = app.getBeansOfType(DataStorePanelInfo.class);

    List<DataStorePanelInfo> fallbacks = new ArrayList<DataStorePanelInfo>();
    for (DataStorePanelInfo provider : providers) {
      Class<?> providerFactoryClass = provider.getFactoryClass();
      if (providerFactoryClass == null) {
        continue;
      }
      if (factoryClass.equals(providerFactoryClass)) {
        return provider;
      } else if (providerFactoryClass.isAssignableFrom(factoryClass)) {
        fallbacks.add(provider);
      }
    }

    if (fallbacks.size() == 1) {
      return fallbacks.get(0);
    } else if (fallbacks.size() > 1) {
      // sort by class hierarchy, pick the closest match
      Collections.sort(
          fallbacks,
          new Comparator<DataStorePanelInfo>() {
            public int compare(DataStorePanelInfo o1, DataStorePanelInfo o2) {
              Class c1 = o1.getFactoryClass();
              Class c2 = o2.getFactoryClass();

              if (c1.equals(c2)) {
                return 0;
              }

              if (c1.isAssignableFrom(c2)) {
                return 1;
              }

              if (c2.isAssignableFrom(c1)) {;
              }

              return -1;
            }
          });
      // check first two and make sure bindings are not equal
      DataStorePanelInfo f1 = fallbacks.get(0);
      DataStorePanelInfo f2 = fallbacks.get(1);

      if (f1.getFactoryClass().equals(f2.getFactoryClass())) {
        String msg =
            "Multiple editor panels for : (" + f1.getFactoryClass() + "): " + f1 + ", " + f2;
        throw new RuntimeException(msg);
      }

      return f1;
    }

    // ok, we don't have a specific one
    return null;
  }