/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.web.data.store; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.net.URL; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.FormComponent; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.ResourceModel; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.ResourcePool; import org.geoserver.platform.GeoServerEnvironment; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.web.data.resource.DataStorePanelInfo; import org.geoserver.web.data.store.panel.CheckBoxParamPanel; import org.geoserver.web.data.store.panel.DropDownChoiceParamPanel; import org.geoserver.web.data.store.panel.LabelParamPanel; import org.geoserver.web.data.store.panel.NamespacePanel; import org.geoserver.web.data.store.panel.ParamPanel; import org.geoserver.web.data.store.panel.PasswordParamPanel; import org.geoserver.web.data.store.panel.TextAreaParamPanel; import org.geoserver.web.data.store.panel.TextParamPanel; import org.geoserver.web.util.MapModel; import org.geoserver.web.wicket.FileExistsValidator; import org.geotools.data.DataAccessFactory; import org.geotools.data.DataAccessFactory.Param; import org.geotools.data.Repository; import org.xml.sax.EntityResolver; /** * A default {@link StoreEditPanel} contribution for the {@link DataStorePanelInfo} extension point * to work on any {@link DataStoreInfo}s. * <p> * This default store parameters panel contributes to the store edit form * {@link DataStorePanelInfo#getComponentClass() extension point} by providing a dynamically and * introspectively generated list of form input fields based on the {@link DataAccessFactory} * parameters for the given {@code DataStoreInfo}. * </p> * * @author Gabriel Roldan * * @see Param * @see ResourcePool#getDataStoreFactory(DataStoreInfo) * @see DataStorePanelInfo * @see AbstractDataAccessPage * @see TextParamPanel * @see CheckBoxParamPanel * @see LabelParamPanel * @see NamespacePanel */ @SuppressWarnings("serial") public class DefaultDataStoreEditPanel extends StoreEditPanel { private static final long serialVersionUID = -1969433619372747193L; /** * 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 and entity resolver params, the resource pool will inject it transparently if (!Repository.class.equals(paramInfo.getBinding()) && !EntityResolver.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(AttributeModifier.replace("title", description)); } item.add(inputComponent); } }; // needed for form components not to loose state paramsList.setReuseItems(true); add(paramsList); } /** * Creates a form input component for the given datastore param based on its type and metadata * properties. * * @param paramMetadata * */ private Panel getInputComponent(final String componentId, final IModel paramsModel, final ParamInfo paramMetadata) { final GeoServerEnvironment gsEnvironment = GeoServerExtensions.bean(GeoServerEnvironment.class); final String paramName = paramMetadata.getName(); final String paramLabel = paramMetadata.getName(); final boolean required = paramMetadata.isRequired(); final boolean deprecated = paramMetadata.isDeprecated(); final Class<?> binding = paramMetadata.getBinding(); final List<Serializable> options = paramMetadata.getOptions(); Panel parameterPanel; if("dbtype".equals(paramName) || "filetype".equals(paramName)) { // skip the two well known discriminators IModel model = new MapModel(paramsModel, paramName); TextParamPanel tp = new TextParamPanel(componentId, model, new ResourceModel(paramLabel, paramLabel), required); tp.setVisible(false); parameterPanel = tp; } else if ("namespace".equals(paramName)) { IModel namespaceModel = new NamespaceParamModel(paramsModel, paramName); IModel paramLabelModel = new ResourceModel(paramLabel, paramLabel); parameterPanel = new NamespacePanel(componentId, namespaceModel, paramLabelModel, true); } else if (options != null && options.size() > 0){ IModel<Serializable> valueModel = new MapModel(paramsModel, paramName); IModel<String> labelModel = new ResourceModel(paramLabel, paramLabel); parameterPanel = new DropDownChoiceParamPanel(componentId, valueModel, labelModel, options, required); } else if (Boolean.class == binding) { // TODO Add prefix for better i18n? parameterPanel = new CheckBoxParamPanel(componentId, new MapModel(paramsModel, paramName), new ResourceModel(paramLabel, paramLabel)); } else if (String.class == binding && paramMetadata.isPassword()) { parameterPanel = new PasswordParamPanel(componentId, new MapModel(paramsModel, paramName), new ResourceModel(paramLabel, paramLabel), required); } else { IModel model; if("url".equalsIgnoreCase(paramName)) { model = new URLModel(paramsModel, paramName); } else { model = new MapModel(paramsModel, paramName); } Panel tp; if(paramMetadata.isLargeText()) { tp = new TextAreaParamPanel(componentId, model, new ResourceModel(paramLabel, paramLabel), required); } else { tp = new TextParamPanel(componentId, model, new ResourceModel(paramLabel, paramLabel), required); } // if it can be a reference to the local filesystem make sure it's valid FormComponent<String> fc = ((ParamPanel) tp).getFormComponent(); // AF: Disable Validator if GeoServer Env Parametrization is enabled! if (paramName.equalsIgnoreCase("url")) { if (gsEnvironment == null || !GeoServerEnvironment.ALLOW_ENV_PARAMETRIZATION) { fc.add(new FileExistsValidator()); } } // make sure the proper value is returned, but don't set it for strings otherwise // we incur in a wicket bug (the empty string is not converter back to a null) // GR: it doesn't work for File neither. // AA: better not mess with files, the converters turn data dir relative to // absolute and bye bye data dir portability // AF: Disable Binding if GeoServer Env Parametrization is enabled! if (gsEnvironment == null || !GeoServerEnvironment.ALLOW_ENV_PARAMETRIZATION) { if (binding != null && !String.class.equals(binding) && !File.class.equals(binding) && !URL.class.equals(binding) && !binding.isArray()) { fc.setType(binding); } } parameterPanel = tp; } Object parameterValue = parameterPanel.getDefaultModelObject(); boolean visible = !(deprecated && isEmpty(parameterValue)); parameterPanel.setVisible(visible); parameterPanel.setVisibilityAllowed(visible); return parameterPanel; } private boolean isEmpty(Object value) { if (value == null) { return true; } else if (value instanceof String) { return ((String) value).isEmpty(); } else { return false; } } /** * Makes sure the file path for shapefiles do start with file:// otherwise * stuff like /home/user/file.shp won't be recognized as valid... * @author aaime * */ private final class URLModel extends MapModel { private URLModel(IModel model, String expression) { super(model, expression); } @Override public void setObject(Object object) { String file = (String) object; if(!file.startsWith("file://") && !file.startsWith("file:") && !file.startsWith("http://")) file = "file://" + file; super.setObject(file); } } }