blob: 401cc6e3bf1466b8a078c779e1d80016fdb16f26 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.
*/
/* $Id$ */
package org.apache.fop.hyphenation;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.InputSource;
/**
* This class is the main entry point to the hyphenation package.
* You can use only the static methods or create an instance.
*
* @author Carlos Villegas <cav@uniscope.co.jp>
*/
public class Hyphenator {
/** logging instance */
protected static Log log = LogFactory.getLog(Hyphenator.class);
private static HyphenationTreeCache hTreeCache = null;
private HyphenationTree hyphenTree = null;
private int remainCharCount = 2;
private int pushCharCount = 2;
/** Enables a dump of statistics. Note: If activated content is sent to System.out! */
private static boolean statisticsDump = false;
/**
* Creates a new hyphenator.
* @param lang the language
* @param country the country (may be null or "none")
* @param leftMin the minimum number of characters before the hyphenation point
* @param rightMin the minimum number of characters after the hyphenation point
*/
public Hyphenator(String lang, String country, int leftMin,
int rightMin) {
hyphenTree = getHyphenationTree(lang, country);
remainCharCount = leftMin;
pushCharCount = rightMin;
}
/** @return the default (static) hyphenation tree cache */
public static synchronized HyphenationTreeCache getHyphenationTreeCache() {
if (hTreeCache == null) {
hTreeCache = new HyphenationTreeCache();
}
return hTreeCache;
}
/**
* Returns a hyphenation tree for a given language and country. The hyphenation trees are
* cached.
* @param lang the language
* @param country the country (may be null or "none")
* @return the hyphenation tree
*/
public static HyphenationTree getHyphenationTree(String lang,
String country) {
return getHyphenationTree(lang, country, null);
}
/**
* Returns a hyphenation tree for a given language and country. The hyphenation trees are
* cached.
* @param lang the language
* @param country the country (may be null or "none")
* @param resolver resolver to find the hyphenation files
* @return the hyphenation tree
*/
public static HyphenationTree getHyphenationTree(String lang,
String country, HyphenationTreeResolver resolver) {
String key = HyphenationTreeCache.constructKey(lang, country);
HyphenationTreeCache cache = getHyphenationTreeCache();
// See if there was an error finding this hyphenation tree before
if (cache.isMissing(key)) {
return null;
}
HyphenationTree hTree;
// first try to find it in the cache
hTree = getHyphenationTreeCache().getHyphenationTree(lang, country);
if (hTree != null) {
return hTree;
}
if (resolver != null) {
hTree = getUserHyphenationTree(key, resolver);
}
if (hTree == null) {
hTree = getFopHyphenationTree(key);
}
// put it into the pattern cache
if (hTree != null) {
cache.cache(key, hTree);
} else {
log.error("Couldn't find hyphenation pattern " + key);
cache.noteMissing(key);
}
return hTree;
}
private static InputStream getResourceStream(String key) {
InputStream is = null;
// Try to use Context Class Loader to load the properties file.
try {
java.lang.reflect.Method getCCL = Thread.class.getMethod(
"getContextClassLoader", new Class[0]);
if (getCCL != null) {
ClassLoader contextClassLoader = (ClassLoader)getCCL.invoke(
Thread.currentThread(),
new Object[0]);
is = contextClassLoader.getResourceAsStream("hyph/" + key
+ ".hyp");
}
} catch (Exception e) {
//ignore, fallback further down
}
if (is == null) {
is = Hyphenator.class.getResourceAsStream("/hyph/" + key
+ ".hyp");
}
return is;
}
private static HyphenationTree readHyphenationTree(InputStream in) {
HyphenationTree hTree = null;
try {
ObjectInputStream ois = new ObjectInputStream(in);
hTree = (HyphenationTree)ois.readObject();
} catch (IOException ioe) {
log.error("I/O error while loading precompiled hyphenation pattern file", ioe);
} catch (ClassNotFoundException cnfe) {
log.error("Error while reading hyphenation object from file", cnfe);
}
return hTree;
}
/**
* Returns a hyphenation tree. This method looks in the resources (getResourceStream) for
* the hyphenation patterns.
* @param key the language/country key
* @return the hyphenation tree or null if it wasn't found in the resources
*/
public static HyphenationTree getFopHyphenationTree(String key) {
HyphenationTree hTree = null;
ObjectInputStream ois = null;
InputStream is = null;
try {
is = getResourceStream(key);
if (is == null) {
if (key.length() == 5) {
String lang = key.substring(0, 2);
is = getResourceStream(lang);
if (is != null) {
if (log.isDebugEnabled()) {
log.debug("Couldn't find hyphenation pattern '"
+ key
+ "'. Using general language pattern '"
+ lang
+ "' instead.");
}
} else {
if (log.isDebugEnabled()) {
log.debug("Couldn't find precompiled hyphenation pattern "
+ lang + " in resources.");
}
return null;
}
} else {
if (log.isDebugEnabled()) {
log.debug("Couldn't find precompiled hyphenation pattern "
+ key + " in resources");
}
return null;
}
}
hTree = readHyphenationTree(is);
} finally {
IOUtils.closeQuietly(ois);
}
return hTree;
}
/**
* Load tree from serialized file or xml file
* using configuration settings
* @param key language key for the requested hyphenation file
* @param hyphenDir base directory to find hyphenation files in
* @return the requested HypenationTree or null if it is not available
*/
public static HyphenationTree getUserHyphenationTree(String key,
String hyphenDir) {
final File baseDir = new File(hyphenDir);
HyphenationTreeResolver resolver = new HyphenationTreeResolver() {
public Source resolve(String href) {
File f = new File(baseDir, href);
return new StreamSource(f);
}
};
return getUserHyphenationTree(key, resolver);
}
/**
* Load tree from serialized file or xml file
* using configuration settings
* @param key language key for the requested hyphenation file
* @param resolver resolver to find the hyphenation files
* @return the requested HypenationTree or null if it is not available
*/
public static HyphenationTree getUserHyphenationTree(String key,
HyphenationTreeResolver resolver) {
HyphenationTree hTree = null;
// I use here the following convention. The file name specified in
// the configuration is taken as the base name. First we try
// name + ".hyp" assuming a serialized HyphenationTree. If that fails
// we try name + ".xml", assumming a raw hyphenation pattern file.
// first try serialized object
String name = key + ".hyp";
Source source = resolver.resolve(name);
if (source != null) {
try {
InputStream in = null;
if (source instanceof StreamSource) {
in = ((StreamSource) source).getInputStream();
}
if (in == null) {
if (source.getSystemId() != null) {
in = new java.net.URL(source.getSystemId()).openStream();
} else {
throw new UnsupportedOperationException("Cannot load hyphenation pattern file"
+ " with the supplied Source object: " + source);
}
}
in = new BufferedInputStream(in);
try {
hTree = readHyphenationTree(in);
} finally {
IOUtils.closeQuietly(in);
}
return hTree;
} catch (IOException ioe) {
if (log.isDebugEnabled()) {
log.debug("I/O problem while trying to load " + name, ioe);
}
}
}
// try the raw XML file
name = key + ".xml";
source = resolver.resolve(name);
if (source != null) {
hTree = new HyphenationTree();
try {
InputStream in = null;
if (source instanceof StreamSource) {
in = ((StreamSource) source).getInputStream();
}
if (in == null) {
if (source.getSystemId() != null) {
in = new java.net.URL(source.getSystemId()).openStream();
} else {
throw new UnsupportedOperationException(
"Cannot load hyphenation pattern file"
+ " with the supplied Source object: " + source);
}
}
if (!(in instanceof BufferedInputStream)) {
in = new BufferedInputStream(in);
}
try {
InputSource src = new InputSource(in);
src.setSystemId(source.getSystemId());
hTree.loadPatterns(src);
} finally {
IOUtils.closeQuietly(in);
}
if (statisticsDump) {
System.out.println("Stats: ");
hTree.printStats();
}
return hTree;
} catch (HyphenationException ex) {
log.error("Can't load user patterns from XML file " + source.getSystemId()
+ ": " + ex.getMessage());
return null;
} catch (IOException ioe) {
if (log.isDebugEnabled()) {
log.debug("I/O problem while trying to load " + name, ioe);
}
return null;
}
} else {
if (log.isDebugEnabled()) {
log.debug("Could not load user hyphenation file for '" + key + "'.");
}
return null;
}
}
/**
* Hyphenates a word.
* @param lang the language
* @param country the optional country code (may be null or "none")
* @param resolver resolver to find the hyphenation files
* @param word the word to hyphenate
* @param leftMin the minimum number of characters before the hyphenation point
* @param rightMin the minimum number of characters after the hyphenation point
* @return the hyphenation result
*/
public static Hyphenation hyphenate(String lang, String country,
HyphenationTreeResolver resolver,
String word,
int leftMin, int rightMin) {
HyphenationTree hTree = getHyphenationTree(lang, country, resolver);
if (hTree == null) {
return null;
}
return hTree.hyphenate(word, leftMin, rightMin);
}
/**
* Hyphenates a word.
* @param lang the language
* @param country the optional country code (may be null or "none")
* @param word the word to hyphenate
* @param leftMin the minimum number of characters before the hyphenation point
* @param rightMin the minimum number of characters after the hyphenation point
* @return the hyphenation result
*/
public static Hyphenation hyphenate(String lang, String country,
String word,
int leftMin, int rightMin) {
return hyphenate(lang, country, null, word, leftMin, rightMin);
}
/**
* Hyphenates a word.
* @param lang the language
* @param country the optional country code (may be null or "none")
* @param resolver resolver to find the hyphenation files
* @param word the word to hyphenate
* @param offset the offset of the first character in the "word" character array
* @param len the length of the word
* @param leftMin the minimum number of characters before the hyphenation point
* @param rightMin the minimum number of characters after the hyphenation point
* @return the hyphenation result
*/
public static Hyphenation hyphenate(String lang, String country,
HyphenationTreeResolver resolver,
char[] word, int offset, int len,
int leftMin, int rightMin) {
HyphenationTree hTree = getHyphenationTree(lang, country, resolver);
if (hTree == null) {
return null;
}
return hTree.hyphenate(word, offset, len, leftMin, rightMin);
}
/**
* Hyphenates a word.
* @param lang the language
* @param country the optional country code (may be null or "none")
* @param word the word to hyphenate
* @param offset the offset of the first character in the "word" character array
* @param len the length of the word
* @param leftMin the minimum number of characters before the hyphenation point
* @param rightMin the minimum number of characters after the hyphenation point
* @return the hyphenation result
*/
public static Hyphenation hyphenate(String lang, String country,
char[] word, int offset, int len,
int leftMin, int rightMin) {
return hyphenate(lang, country, null, word, offset, len, leftMin, rightMin);
}
/**
* Sets the minimum number of characters before the hyphenation point
* @param min the number of characters
*/
public void setMinRemainCharCount(int min) {
remainCharCount = min;
}
/**
* Sets the minimum number of characters after the hyphenation point
* @param min the number of characters
*/
public void setMinPushCharCount(int min) {
pushCharCount = min;
}
/**
* Sets the language and country for the hyphenation process.
* @param lang the language
* @param country the country (may be null or "none")
*/
public void setLanguage(String lang, String country) {
hyphenTree = getHyphenationTree(lang, country);
}
/**
* Hyphenates a word.
* @param word the word to hyphenate
* @param offset the offset of the first character in the "word" character array
* @param len the length of the word
* @return the hyphenation result
*/
public Hyphenation hyphenate(char[] word, int offset, int len) {
if (hyphenTree == null) {
return null;
}
return hyphenTree.hyphenate(word, offset, len, remainCharCount,
pushCharCount);
}
/**
* Hyphenates a word.
* @param word the word to hyphenate
* @return the hyphenation result
*/
public Hyphenation hyphenate(String word) {
if (hyphenTree == null) {
return null;
}
return hyphenTree.hyphenate(word, remainCharCount, pushCharCount);
}
}