/*
* #!
* Ontopia Navigator
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* Licensed 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 net.ontopia.topicmaps.nav2.portlets.pojos;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.io.IOException;
import java.net.URL;
import org.xml.sax.InputSource;
import net.ontopia.utils.StringUtils;
import net.ontopia.utils.StringifierIF;
import net.ontopia.infoset.core.LocatorIF;
import net.ontopia.infoset.impl.basic.URILocator;
import net.ontopia.topicmaps.core.TopicIF;
import net.ontopia.topicmaps.core.TopicMapIF;
import net.ontopia.topicmaps.xml.TMXMLReader;
import net.ontopia.topicmaps.utils.TopicStringifiers;
import net.ontopia.topicmaps.query.core.QueryResultIF;
import net.ontopia.topicmaps.query.core.InvalidQueryException;
import net.ontopia.topicmaps.query.utils.QueryWrapper;
import net.ontopia.topicmaps.query.utils.RowMapperIF;
/**
* PUBLIC: A component for including links to relevant pages from
* other web sites via TMRAP.
*/
public class TMRAP {
private Collection servers;
private StringifierIF strify;
private static Map cache = new HashMap();
/**
* PUBLIC: Creates the component and configures it with a set of
* servers to query.
* @param servers a collection of TMRAP endpoint URIs as strings
*/
public TMRAP(Collection servers) {
this.servers = servers;
this.strify = TopicStringifiers.getDefaultStringifier();
}
/**
* PUBLIC: Sends a query, returning a model of the result.
*/
public Collection query(TopicIF topic)
throws IOException, InvalidQueryException {
Collection psis = new ArrayList();
Iterator it = topic.getSubjectIdentifiers().iterator();
while (it.hasNext()) {
LocatorIF psi = (LocatorIF) it.next();
psis.add(psi.getAddress());
}
return query(psis);
}
/**
* PUBLIC: Sends a query, returning a model of the result.
* @param psis a collection of PSIs as strings
* @return a list of Server objects
*/
public Collection query(Collection psis)
throws IOException, InvalidQueryException {
if (psis.isEmpty())
return Collections.EMPTY_SET;
String psikey = makeKey(psis);
Collection model = new ArrayList();
Iterator it = servers.iterator();
while (it.hasNext()) {
String endpoint = (String) it.next();
// is it in the cache?
if (cache.containsKey(endpoint + psikey)) {
CacheEntry entry = (CacheEntry) cache.get(endpoint + psikey);
model.add(entry.getObject());
continue;
}
// not in cache, so go look it up
InputSource src = getInputSource(endpoint, psis);
TMXMLReader reader =
new TMXMLReader(src, new URILocator(src.getSystemId()));
TopicMapIF topicmap = reader.read();
QueryWrapper qw = new QueryWrapper(topicmap);
qw.setDeclarations("using tmrap for i\"http://psi.ontopia.net/tmrap/\" ");
Server server = (Server)
qw.queryForObject("instance-of($S, tmrap:server)?", new Factory());
model.add(server);
qw.queryForList("instance-of($TM, tmrap:topicmap)?",
new Factory(server));
// process the topic maps
Iterator it2 = server.getTopicMaps().iterator();
while (it2.hasNext()) {
TopicMap tm = (TopicMap) it2.next();
Map params = qw.makeParams("tm", tm.getTopic());
qw.queryForList("tmrap:contained-in(%tm% : tmrap:container, $P : tmrap:containee)?", new Factory(tm), params);
}
// update the cache
cache.put(endpoint + psikey, new CacheEntry(server));
}
return model;
}
public Collection getAllPages(Collection model) {
Collection pages = new ArrayList();
Iterator it = model.iterator();
while (it.hasNext()) {
Server server = (Server) it.next();
pages.addAll(server.getPages());
}
return pages;
}
public Map getPageMap(Collection model) {
Map map = new HashMap();
Iterator it = model.iterator();
while (it.hasNext()) {
Server server = (Server) it.next();
Iterator it2 = server.getPages().iterator();
while (it2.hasNext()) {
Page page = (Page) it2.next();
Collection pages;
if (map.containsKey(page.getTypeURI()))
pages = (Collection) map.get(page.getTypeURI());
else {
pages = new ArrayList();
map.put(page.getTypeURI(), pages);
}
pages.add(page);
}
}
return map;
}
/* ----- MODEL ----------------------------------------------------- */
public class Server {
private TopicIF topic;
private Collection topicmaps;
public Server(TopicIF topic) {
this.topic = topic;
this.topicmaps = new ArrayList();
}
public String getName() {
return strify.toString(topic);
}
public TopicIF getTopic() {
return topic;
}
public Collection getTopicMaps() {
return topicmaps;
}
public Collection getPages() {
Collection pages = new ArrayList();
Iterator it = topicmaps.iterator();
while (it.hasNext()) {
TopicMap tm = (TopicMap) it.next();
pages.addAll(tm.getPages());
}
return pages;
}
private void addTopicMap(TopicMap tm) {
topicmaps.add(tm);
}
}
public class TopicMap {
private Server server;
private TopicIF topic;
private Collection pages;
public TopicMap(Server server, TopicIF topic) {
this.server = server;
this.topic = topic;
this.pages = new ArrayList();
server.addTopicMap(this);
}
public TopicIF getTopic() {
return topic;
}
public Collection getPages() {
return pages;
}
private void addPage(Page page) {
pages.add(page);
}
}
public class Page { // could be edit-page, view-page, or something else
private TopicMap topicmap;
private TopicIF topic;
public Page(TopicMap topicmap, TopicIF topic) {
this.topicmap = topicmap;
this.topic = topic;
topicmap.addPage(this);
}
public String getName() {
return strify.toString(topic);
}
public String getURI() {
LocatorIF loc = topic.getSubjectLocators().iterator().next();
return loc.getAddress();
}
public String getTypeURI() { // tells us if it's edit-page, view-page ...
if (topic.getTypes().isEmpty())
return null;
TopicIF type = topic.getTypes().iterator().next();
if (type.getSubjectIdentifiers().isEmpty())
return null;
LocatorIF psi = type.getSubjectIdentifiers().iterator().next();
return psi.getAddress();
}
}
class Factory implements RowMapperIF {
private Server server;
private TopicMap topicmap;
private Factory() {
}
private Factory(Server server) {
this.server = server;
}
private Factory(TopicMap topicmap) {
this.topicmap = topicmap;
}
public Object mapRow(QueryResultIF result, int rowno) {
if (server == null && topicmap == null)
return new Server((TopicIF) result.getValue(0));
if (server != null && topicmap == null)
return new TopicMap(server, (TopicIF) result.getValue(0));
else
return new Page(topicmap, (TopicIF) result.getValue(0));
}
}
/* ----- CACHE ENTRY ----------------------------------------------- */
class CacheEntry {
private long time;
private Server object;
public CacheEntry(Server object) {
this.object = object;
this.time = System.currentTimeMillis();
}
public Server getObject() {
return object;
}
}
/* ----- INTERNAL -------------------------------------------------- */
private InputSource getInputSource(String endpoint, Collection psis)
throws IOException {
String uri = addParameters(endpoint, psis);
URL url = new URL(uri);
InputSource src = new InputSource(uri);
src.setByteStream(url.openStream());
return src;
}
private String addParameters(String endpoint, Collection psis) {
StringBuilder buf = new StringBuilder(endpoint);
buf.append('?');
Iterator it = psis.iterator();
while (it.hasNext()) {
String psi = (String) it.next();
buf.append("identifier=");
buf.append(StringUtils.replace(psi, "#", "%23"));
if (it.hasNext())
buf.append("&");
}
return buf.toString();
}
private String makeKey(Collection psis) {
List uris = new ArrayList(psis);
Collections.sort(uris);
StringBuilder sb = new StringBuilder();
for (int ix = 0; ix < uris.size(); ix++)
sb.append((String) uris.get(ix));
return sb.toString();
}
}