/*
* 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.complexscripts.fonts;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.ScriptContextTester;
// CSOFF: EmptyForIteratorPadCheck
// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
// CSOFF: NoWhitespaceAfterCheck
// CSOFF: ParameterNumberCheck
// CSOFF: SimplifyBooleanReturnCheck
/**
*
Base class for all advanced typographic glyph tables.
*
* This work was originally authored by Glenn Adams (gadams@apache.org).
*/
public class GlyphTable {
/** logging instance */
private static final Log log = LogFactory.getLog(GlyphTable.class);
/** substitution glyph table type */
public static final int GLYPH_TABLE_TYPE_SUBSTITUTION = 1;
/** positioning glyph table type */
public static final int GLYPH_TABLE_TYPE_POSITIONING = 2;
/** justification glyph table type */
public static final int GLYPH_TABLE_TYPE_JUSTIFICATION = 3;
/** baseline glyph table type */
public static final int GLYPH_TABLE_TYPE_BASELINE = 4;
/** definition glyph table type */
public static final int GLYPH_TABLE_TYPE_DEFINITION = 5;
// (optional) glyph definition table in table types other than glyph definition table
private GlyphTable gdef;
// map from lookup specs to lists of strings, each of which identifies a lookup table (consisting of one or more subtables)
private Map/*>*/ lookups;
// map from lookup identifiers to lookup tables
private Map/**/ lookupTables;
// cache for lookups matching
private Map/*>>*/ matchedLookups;
// if true, then prevent further subtable addition
private boolean frozen;
/**
* Instantiate glyph table with specified lookups.
* @param gdef glyph definition table that applies
* @param lookups map from lookup specs to lookup tables
*/
public GlyphTable(GlyphTable gdef, Map/*>*/ lookups) {
if ((gdef != null) && ! (gdef instanceof GlyphDefinitionTable)) {
throw new AdvancedTypographicTableFormatException("bad glyph definition table");
} else if (lookups == null) {
throw new AdvancedTypographicTableFormatException("lookups must be non-null map");
} else {
this.gdef = gdef;
this.lookups = lookups;
this.lookupTables = new LinkedHashMap/*>*/();
this.matchedLookups = new HashMap/*>>*/();
}
}
/**
* Obtain glyph definition table.
* @return (possibly null) glyph definition table
*/
public GlyphDefinitionTable getGlyphDefinitions() {
return (GlyphDefinitionTable) gdef;
}
/**
* Obtain list of all lookup specifications.
* @return (possibly empty) list of all lookup specifications
*/
public List/**/ getLookups() {
return matchLookupSpecs("*", "*", "*");
}
/**
* Obtain ordered list of all lookup tables, where order is by lookup identifier, which
* lexicographic ordering follows the lookup list order.
* @return (possibly empty) ordered list of all lookup tables
*/
public List/**/ getLookupTables() {
TreeSet/**/ lids = new TreeSet/**/(lookupTables.keySet());
List/**/ ltl = new ArrayList/**/(lids.size());
for (Iterator it = lids.iterator(); it.hasNext(); ) {
String lid = (String) it.next();
ltl.add(lookupTables.get(lid));
}
return ltl;
}
/**
* Obtain lookup table by lookup id. This method is used by test code, and provides
* access to embedded lookups not normally accessed by {script, language, feature} lookup spec.
* @param lid lookup id
* @return table associated with lookup id or null if none
*/
public LookupTable getLookupTable(String lid) {
return (LookupTable) lookupTables.get(lid);
}
/**
* Add a subtable.
* @param subtable a (non-null) glyph subtable
*/
protected void addSubtable(GlyphSubtable subtable) {
// ensure table is not frozen
if (frozen) {
throw new IllegalStateException("glyph table is frozen, subtable addition prohibited");
}
// set subtable's table reference to this table
subtable.setTable(this);
// add subtable to this table's subtable collection
String lid = subtable.getLookupId();
if (lookupTables.containsKey(lid)) {
LookupTable lt = (LookupTable) lookupTables.get(lid);
lt.addSubtable(subtable);
} else {
LookupTable lt = new LookupTable(lid, subtable);
lookupTables.put(lid, lt);
}
}
/**
* Freeze subtables, i.e., do not allow further subtable addition, and
* create resulting cached state.
*/
protected void freezeSubtables() {
if (! frozen) {
for (Iterator it = lookupTables.values().iterator(); it.hasNext(); ) {
LookupTable lt = (LookupTable) it.next();
lt.freezeSubtables(lookupTables);
}
frozen = true;
}
}
/**
* Match lookup specifications according to