/*
* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. licenses this file to You 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 com.esri.gpt.control.webharvest.client.agportal;
import com.esri.gpt.catalog.arcgis.agportal.client.SearchClient;
import com.esri.gpt.catalog.arcgis.agportal.client.SearchClient.SearchResult;
import com.esri.gpt.catalog.arcgis.agportal.itemInfo.ESRI_ItemInformation;
import com.esri.gpt.control.webharvest.IterationContext;
import com.esri.gpt.framework.geometry.Envelope;
import com.esri.gpt.framework.isodate.IsoDateFormat;
import com.esri.gpt.framework.resource.api.Publishable;
import com.esri.gpt.framework.resource.api.Resource;
import com.esri.gpt.framework.resource.api.SourceUri;
import com.esri.gpt.framework.resource.common.CommonPublishable;
import com.esri.gpt.framework.resource.common.StringUri;
import com.esri.gpt.framework.resource.query.Criteria;
import com.esri.gpt.framework.util.ReadOnlyIterator;
import com.esri.gpt.framework.util.Val;
import java.io.IOException;
import java.util.*;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
import org.json.JSONException;
import org.xml.sax.SAXException;
/**
* ArcGIS Portal folders.
* NOTE! This is EXPERIMENTAL feature. It might be removed at any time in the future.
*/
public class AgpFolders implements Iterable<Resource> {
/** logger */
private static final Logger LOGGER = Logger.getLogger(AgpFolders.class.getCanonicalName());
/** iteration context */
private IterationContext context;
/** service info */
private AgpInfo info;
/** query criteria */
private Criteria criteria;
/**
* Creates new instance of folders.
* @param context iteration context
* @param info service info
* @param criteria query criteria
*/
public AgpFolders(IterationContext context, AgpInfo info, Criteria criteria) {
if (context == null) {
throw new IllegalArgumentException("No context provided.");
}
if (info == null) {
throw new IllegalArgumentException("No info provided.");
}
this.context = context;
this.info = info;
this.criteria = criteria;
}
@Override
public Iterator<Resource> iterator() {
return new AgpFolderIterator();
}
/**
* OAI folders iterator.
*/
private class AgpFolderIterator extends ReadOnlyIterator<Resource> {
SearchResult sr = null;
/** resumption Id */
private int resumptionId = 1;
/** next records */
private Iterable<Resource> nextAgpRecords = null;
/** records counter */
private int recs;
/** no more records*/
private boolean noMore;
@Override
public boolean hasNext() {
if (!noMore && nextAgpRecords == null) {
if (resumptionId > 0) {
try {
advanceToNextRecords();
} catch (IOException ex) {
noMore = true;
context.onIterationException(ex);
}
} else {
noMore = true;
}
}
return !noMore;
}
@Override
public Resource next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
final Iterable<Resource> records = nextAgpRecords;
nextAgpRecords = null;
return new Resource() {
@Override
public Iterable<Resource> getNodes() {
return records;
}
};
}
/**
* Advances to the next set of records.
* @throws IOException if advancing fails
* @throws JSONException
* @throws SAXException
* @throws ParserConfigurationException
*/
private void advanceToNextRecords() throws IOException {
LOGGER.finer("Advancing to the next group of records.");
final String startDcTemplate = "<?xml version=\"1.0\"?><rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:ows=\"http://www.opengis.net/ows\" xmlns:dct=\"http://purl.org/dc/terms/\" xmlns:dcmiBox=\"http://dublincore.org/documents/2000/07/11/dcmi-box/\">";
final String endDcTemplate = "</rdf:Description></rdf:RDF>";
if (sr==null) {
SearchClient sc = new SearchClient(info.getUrl());
sr = sc.listAll();
} else {
sr = sr.next();
}
if (sr!=null) {
ArrayList<Resource> resources = new ArrayList<Resource>();
for (final ESRI_ItemInformation ii : sr.getItems()) {
final StringBuilder document = new StringBuilder();
final Appender app = new Appender(document);
recs++;
boolean maxReached = criteria != null && criteria.getMaxRecords() != null && recs > criteria.getMaxRecords();
if (maxReached) {
noMore = true;
break;
}
Date modifiedDate = ii.getModifiedDate();
if (criteria!=null && criteria.getFromDate()!=null && modifiedDate!=null && criteria.getFromDate().before(modifiedDate)) {
continue;
}
Publishable publishable = new CommonPublishable() {
private String itemInfoUrl = info.getUrl().replaceAll("/$", "") + "/content/items/" + ii.getId() + "/info/iteminfo.xml";
private StringUri uri = new StringUri(formatUuid(ii.getId()));
@Override
public SourceUri getSourceUri() {
return uri;
}
@Override
public String getContent() throws IOException {
try {
document.append(startDcTemplate);
document.append("<rdf:Description rdf:about=\"").append(itemInfoUrl).append("\">");
if (!app.append("dc:title", ii.getTitle())) {
app.append("dc:title", ii.getName());
}
if (ii.getModifiedDate()!=null) {
app.append("dc:date", new IsoDateFormat().format(ii.getModifiedDate()));
}
app.append("dc:identifier", ii.getId());
if (!app.append("dc:description", ii.getDescription())) {
app.append("dc:description", ii.getSnippet());
}
app.append("dct:references", ii.getUrl());
app.append("dct:references", itemInfoUrl);
app.append("dc:creator", ii.getOwner());
app.append("dct:accessRights", ii.getAccess());
app.append("dc:language", ii.getCulture());
app.append("dc:type", ii.getTypeKeywords());
app.append("dc:type", ii.getTags());
app.append("dc:subject", ii.getType());
if (ii.getExtent()!=null && ii.getExtent().isValid()) {
Envelope e = ii.getExtent();
document.append("<ows:WGS84BoundingBox>");
document.append("<ows:LowerCorner>");
document.append(Double.toString(e.getMinX())).append(" ").append(Double.toString(e.getMinY()));
document.append("</ows:LowerCorner>");
document.append("<ows:UpperCorner>");
document.append(Double.toString(e.getMaxX())).append(" ").append(Double.toString(e.getMaxY()));
document.append("</ows:UpperCorner>");
document.append("</ows:WGS84BoundingBox>");
}
document.append(endDcTemplate);
return document.toString();
} catch (Exception e) {
throw new IOException("Error occured while retrieving metadata, cause : " + e.getMessage());
}
}
};
resources.add(publishable);
}
nextAgpRecords = resources;
} else {
noMore = true;
}
}
}
private String formatUuid(String uuid) {
uuid = Val.chkStr(uuid);
String value = uuid.replaceAll("[^0-9a-fA-F]", "");
if (value.length()==32) {
StringBuilder sb = new StringBuilder();
sb.append(value.substring(0, 8));
sb.append("-");
sb.append(value.substring(8, 12));
sb.append("-");
sb.append(value.substring(12, 16));
sb.append("-");
sb.append(value.substring(16, 20));
sb.append("-");
sb.append(value.substring(20));
value = "{"+sb.toString()+"}";
} else {
value = uuid;
}
return value;
}
private class Appender {
private StringBuilder sb;
public Appender(final StringBuilder sb) {
this.sb = sb;
}
public boolean append(String tagName, String value) {
tagName = Val.chkStr(tagName);
if (tagName.length()==0) return false;
value = Val.chkStr(value);
if (value.length()==0 || value.equalsIgnoreCase("null")) return false;
sb.append("<").append(tagName).append(">");
sb.append(Val.escapeXml(value));
sb.append("</").append(tagName).append(">");
return true;
}
public void append(String tagName, List<String> values) {
for (String v : values) {
append(tagName, v);
}
}
}
}