/* (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.layer; import com.google.common.collect.Lists; import org.apache.wicket.extensions.markup.html.repeater.util.SortParam; import org.apache.wicket.model.IModel; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.Predicates; import org.geoserver.catalog.util.CloseableIterator; import org.geoserver.catalog.util.CloseableIteratorAdapter; import org.geoserver.web.wicket.GeoServerDataProvider; import org.opengis.filter.Filter; import org.opengis.filter.sort.SortBy; import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; import java.util.List; import static org.geoserver.catalog.Predicates.sortBy; /** * Provides a filtered, sorted view over the catalog layers. * <p> * <!-- Implementation detail: This class overrides the following methods in * order to leverage the Catalog filtering and paging support: * <ul> * <li> {@link #size()}: in order to call {@link Catalog#count(Class, Filter)} * with any filter criteria set on the page * <li> {@link #fullSize()}: in order to call * {@link Catalog#count(Class, Filter)} with {@link Predicates#acceptAll()} * <li>{@link #iterator}: in order to ask the catalog for paged and sorted * contents directly through * {@link Catalog#list(Class, Filter, Integer, Integer, SortBy)} * <li> {@link #getItems()} throws an unsupported operation exception, as given * the above it should not be called * </ul> * --> * * @author Andrea Aime - OpenGeo */ @SuppressWarnings("serial") public class LayerProvider extends GeoServerDataProvider<LayerInfo> { static final Property<LayerInfo> TYPE = new BeanProperty<>("type", "type"); static final Property<LayerInfo> STORE = new BeanProperty<>( "store", "resource.store.name"); static final Property<LayerInfo> NAME = new BeanProperty<>("name", "name"); static final Property<LayerInfo> TITLE = new BeanProperty<>("title", "title"); /** * A custom property that uses the derived enabled() property instead of isEnabled() to account * for disabled resource/store */ static final Property<LayerInfo> ENABLED = new AbstractProperty<LayerInfo>("enabled") { public Boolean getPropertyValue(LayerInfo item) { return Boolean.valueOf(item.enabled()); } }; static final Property<LayerInfo> SRS = new BeanProperty<LayerInfo>("SRS", "resource.SRS") { /** * We roll a custom comparator that treats the numeric part of the * code as a number */ public java.util.Comparator<LayerInfo> getComparator() { return new Comparator<LayerInfo>() { public int compare(LayerInfo o1, LayerInfo o2) { // split out authority and code String[] srs1 = o1.getResource().getSRS().split(":"); String[] srs2 = o2.getResource().getSRS().split(":"); // use sign to control sort order if (srs1[0].equalsIgnoreCase(srs2[0]) && srs1.length > 1 && srs2.length > 1) { try { // in case of same authority, compare numbers return new Integer(srs1[1]).compareTo(new Integer( srs2[1])); } catch(NumberFormatException e) { // a handful of codes are not numeric, // handle the general case as well return srs1[1].compareTo(srs2[1]); } } else { // compare authorities return srs1[0].compareToIgnoreCase(srs2[0]); } } }; } }; static final List<Property<LayerInfo>> PROPERTIES = Arrays.asList(TYPE, TITLE, NAME, STORE, ENABLED, SRS); @Override protected List<LayerInfo> getItems() { // forced to implement this method as its abstract in the super class throw new UnsupportedOperationException( "This method should not be being called! " + "We use the catalog streaming API"); } @Override protected List<Property<LayerInfo>> getProperties() { return PROPERTIES; } @Override public IModel<LayerInfo> newModel(LayerInfo object) { return new LayerDetachableModel(object); } @Override protected Comparator<LayerInfo> getComparator(SortParam<?> sort) { return super.getComparator(sort); } @Override public long size() { Filter filter = getFilter(); int count = getCatalog().count(LayerInfo.class, filter); return count; } @Override public int fullSize() { Filter filter = Predicates.acceptAll(); int count = getCatalog().count(LayerInfo.class, filter); return count; } @Override public Iterator<LayerInfo> iterator(final long first, final long count) { Iterator<LayerInfo> iterator = filteredItems(first, count); if (iterator instanceof CloseableIterator) { // don't know how to force wicket to close the iterator, lets return // a copy. Shouldn't be much overhead as we're paging try { return Lists.newArrayList(iterator).iterator(); } finally { CloseableIteratorAdapter.close(iterator); } } else { return iterator; } } /** * Returns the requested page of layer objects after applying any keyword * filtering set on the page */ private Iterator<LayerInfo> filteredItems(Long first, Long count) { final Catalog catalog = getCatalog(); // global sorting final SortParam<?> sort = getSort(); final Property<LayerInfo> property = getProperty(sort); SortBy sortOrder = null; if (sort != null) { if(property instanceof BeanProperty){ final String sortProperty = ((BeanProperty<LayerInfo>)property).getPropertyPath(); sortOrder = sortBy(sortProperty, sort.isAscending()); }else if(property == ENABLED){ sortOrder = sortBy("enabled", sort.isAscending()); } } if(first>Integer.MAX_VALUE || first<Integer.MIN_VALUE || count>Integer.MAX_VALUE || count<Integer.MIN_VALUE) { throw new IllegalArgumentException(); // TODO Possibly change catalog API to use long } final Filter filter = getFilter(); //our already filtered and closeable iterator Iterator<LayerInfo> items = catalog.list(LayerInfo.class, filter, first.intValue(), count.intValue(), sortOrder); return items; } }