/*
* #!
* Ontopia Engine
* #-
* 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.impl.utils;
import java.io.IOException;
import java.io.Reader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PushbackInputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.net.URL;
import org.xml.sax.InputSource;
import net.ontopia.infoset.core.LocatorIF;
import net.ontopia.topicmaps.core.TopicMapIF;
import net.ontopia.topicmaps.core.TopicMapReaderIF;
import net.ontopia.topicmaps.core.TopicMapStoreIF;
import net.ontopia.topicmaps.core.TopicMapStoreFactoryIF;
import net.ontopia.topicmaps.core.TopicMapImporterIF;
import net.ontopia.topicmaps.impl.basic.InMemoryStoreFactory;
import net.ontopia.topicmaps.utils.SameStoreFactory;
/**
* INTERNAL: Common abstract superclass for topic map readers.
*/
public abstract class AbstractTopicMapReader
implements TopicMapReaderIF, TopicMapImporterIF {
protected InputSource source;
protected LocatorIF base_address;
protected TopicMapStoreFactoryIF store_factory;
/**
* PUBLIC: Gets the SAX input source used by the reader.
*/
public InputSource getInputSource() {
return source;
}
/**
* PUBLIC: Sets the SAX input source used by the reader.
*/
public void setInputSource(InputSource source) {
this.source = source;
}
/**
* PUBLIC: Gets the top level base address of the input source.
*/
public LocatorIF getBaseAddress() {
return base_address;
}
/**
* PUBLIC: Sets the top level base address of the input source.</p>
*
* The top level base address is used to resolve relative addresses
* during input source processing. This property need not be set if
* the input source specifies the base address.</p>
*/
public void setBaseAddress(LocatorIF base_address) {
this.base_address = base_address;
}
/**
* PUBLIC: Gets the store factory which will be used to create stores.
*/
public TopicMapStoreFactoryIF getStoreFactory() {
// Initialize default factory
if (store_factory == null) {
store_factory = new InMemoryStoreFactory();
}
return store_factory;
}
/**
* PUBLIC: Sets the store factory which will be used to create stores.</p>
*
* <p>Default: {@link
* net.ontopia.topicmaps.impl.basic.InMemoryStoreFactory}</p>
*
* @param store_factory The store factory to use. If the parameter
* is null the default store factory will be used.
*/
public void setStoreFactory(TopicMapStoreFactoryIF store_factory) {
this.store_factory = store_factory;
}
// ==== READER IMPLEMENTATION ====
public TopicMapIF read() throws IOException {
return read(getStoreFactory());
}
public Collection<TopicMapIF> readAll() throws IOException {
return readAll(getStoreFactory());
}
protected Collection<TopicMapIF> readAll(TopicMapStoreFactoryIF store_factory)
throws IOException {
// we assume the data source must by necessity contain only a
// single topic map; override if this assumption is wrong.
return Collections.singleton(read(store_factory));
}
// the real implementation, specific to each syntax
protected abstract TopicMapIF read(TopicMapStoreFactoryIF store_factory)
throws IOException;
/**
* Default implemenentation does not accept any additional properties
* @param properties
*/
public void setAdditionalProperties(Map<String, Object> properties) {
// no-op
}
// ==== IMPORTER IMPLEMENTATION ====
public void importInto(TopicMapIF topicmap) throws IOException {
// Check that store is ok
TopicMapStoreIF store = topicmap.getStore();
if (store == null)
throw new IOException("Topic map not connected to a store.");
// Use a store factory that always returns the same topic
// map. This makes sure that all topic maps found inside the
// source document will be imported into the document.
// Read all topic maps from the source.
readAll(new SameStoreFactory(store));
}
// ===== HELPER METHODS
/**
* INTERNAL: Creates a correctly configured Reader from a SAX
* InputSource object.
*/
public static Reader makeReader(InputSource source, EncodingSnifferIF sniffer)
throws IOException {
Reader reader = source.getCharacterStream();
if (reader == null) {
if (source.getByteStream() != null)
reader = makeReader(source.getByteStream(), source.getEncoding(), sniffer);
else {
URL url = new URL(source.getSystemId());
reader = makeReader(url.openStream(), null, sniffer);
}
}
return reader;
}
public static Reader makeReader(InputStream stream, String encoding,
EncodingSnifferIF sniffer) throws IOException {
if (encoding == null) {
// BEWARE: next statements means we make a reader from the pushbackstream!
// this is crucial, since otherwise the first 50 bytes of the stream are lost
stream = new PushbackInputStream(stream, 50);
encoding = sniffer.guessEncoding((PushbackInputStream) stream);
}
return new InputStreamReader(stream, encoding);
}
}