package folioxml.slx;
import folioxml.core.InvalidMarkupException;
import folioxml.core.TokenUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* Works with both XML and SLX valid. Parses object definitions from the root record into a table. Expands <OB> object references to include the object definition data.
* Unless you plan on doing dynamic reference expansion using javascript, you probably want your objects inlined. Object definitions don't include much more than the path and type of the data.
*
* @author nathanael
*/
public class ObjectResolver {
SlxRecord root = null;
public ObjectResolver(SlxRecord rootRecord) {
root = rootRecord;
}
Map<String, Map<String, String>> defs = null;
private void parseDefs() throws IOException, InvalidMarkupException {
if (defs != null) return; //Don't parse twice
defs = new HashMap<String, Map<String, String>>();
ISlxTokenReader reader = root.getTokenReaderForRecord();
while (reader.canRead()) {
SlxToken t = reader.read();
if (t == null) break;
if (t.matches("object-def")) {
String key = t.get("name").toLowerCase(Locale.ENGLISH);
Map<String, String> attrs = new HashMap<String, String>();
attrs.putAll(t.getAttributes());
attrs.remove("name"); //Remove the name attribute. No longer wanted... it's the key.
if ("true".equalsIgnoreCase(attrs.get("replaceDefinition")) || !defs.containsKey(key)) {
defs.put(key, attrs); //Store if no existing key exists, or if replaceDefiniton is specified.
attrs.remove("replaceDefinition"); //No need for it anymore - it's been used.
}
}
}
//Done.
}
/**
* Fixes all the tokens within the record.
*
* @param r
* @throws InvalidMarkupException
* @throws IOException
*/
public void fixRecord(SlxRecord r) throws IOException, InvalidMarkupException {
ISlxTokenReader reader = r.getTokenReaderForRecord();
while (reader.canRead()) {
SlxToken t = reader.read();
if (t == null) break;
fixToken(t);
}
}
/**
* If the specified token is an <object> tag, it is resolved, and modified to include definition data. An exception is thrown if the reference doesn't exist in the definition.
*
* @param t
* @throws InvalidMarkupException
* @throws IOException
*/
public void fixToken(SlxToken t) throws InvalidMarkupException, IOException {
if (!t.isTag()) return;
if (!t.matches("object|link")) return;
String key = null;
if (t.matches("link")) {
key = t.get("objectName"); //OL (Object link)
if (key == null) key = t.get("dataLink"); //DL (Data link)
if (key == null || t.get("infobase") != null)
return; //Not the right type of link. Only local infobase links are supported.
} else {
key = t.get("name");
}
key = key.toLowerCase(Locale.ENGLISH);
parseDefs();//Make sure defs are parsed.
Map<String, String> newAttrs = defs.get(key);
if (newAttrs == null)
throw new InvalidMarkupException("Object named \"" + t.get("name") + "\" was not found in the definiton.", t);
//TODO: Isn't type a required attribute?
if (t.matches("object") && newAttrs.get("type") != null && newAttrs.get("type").equalsIgnoreCase("data-link"))
throw new InvalidMarkupException("Data-link objects cannot be embedded, only linked to.");
//Ok, replace with the new attrs. (is this what we want)?
t.getAttributes().putAll(newAttrs);
//Link tags need special help.
if (t.matches("link")) {
//Type may be any of 'folio', 'data-link', 'ole', or 'class-object', as the attributes from the object definition have been merged in
if (TokenUtils.fastMatches("folio", t.get("type"))) {
//<link class="Object" handler="Bitmap" objectName="Faircom Logo - Used in Folio Validator" src="FolioHlp\FFF6.OB" type="folio">
//Rename 'src' to 'href'.
if (t.get("src") != null) {
t.set("href", t.get("src"));
t.removeAttr("src");
}
} else if (TokenUtils.fastMatches("data-link", t.get("type"))) {
//Rename 'src' to 'href'.
if (t.get("src") != null) {
t.set("href", t.get("src"));
t.removeAttr("src");
}
} else
throw new InvalidMarkupException("OL (Object Links) cannot point to OLE or class objects. Type=" + t.get("type"));
}
t.updateMarkup(); //We're done! Object resolved - definition arguments have been copied. The 'type' value will be replaced, but that's good.
}
}