package org.emile.cirilo.business;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.net.URL;
import java.util.*;
import java.net.URLConnection;
import javax.imageio.ImageIO;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;
import org.apache.log4j.Logger;
import org.emile.cirilo.Common;
import org.emile.cirilo.ServiceNames;
import org.emile.cirilo.User;
import org.emile.cirilo.ecm.repository.Repository;
import org.emile.cirilo.ecm.templates.TemplateSubsystem;
import org.emile.cirilo.utils.ImageTools;
import org.emile.cirilo.utils.eXist;
import org.emile.cirilo.business.IIIFFactory;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.transform.JDOMResult;
import org.jdom.transform.JDOMSource;
import org.jdom.xpath.XPath;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.base.Resource;
import org.xmldb.api.modules.XMLResource;
import voodoosoft.jroots.core.CPropertyService;
import voodoosoft.jroots.core.CServiceProvider;
import voodoosoft.jroots.dialog.CDefaultGuiAdapter;
public class METS {
private static Logger log = Logger.getLogger(METS.class);
private Document mets;
private Document viewer;
private File file;
private String collection;
private Format format;
private XMLOutputter outputter;
private FileWriter logger;
private String PID;
private TemplateSubsystem temps;
private String URI;
private User user;
private boolean onlyValidate;
private boolean mode;
private CPropertyService props;
private String xuser;
public METS(FileWriter logger, boolean validate, boolean mode) {
try {
temps = (TemplateSubsystem) CServiceProvider.getService(ServiceNames.TEMPLATESUBSYSTEM);
props = (CPropertyService) CServiceProvider.getService(ServiceNames.PROPERTIES);
user = (User) CServiceProvider.getService(ServiceNames.CURRENT_USER);
URI = user.getExistUrl();
format = Format.getRawFormat();
format.setEncoding("UTF-8");
outputter = new XMLOutputter(format);
this.logger = logger;
this.onlyValidate = validate;
this.mode = mode;
this.xuser = null;
} catch (Exception e) {
log.error(e.getLocalizedMessage(),e);
}
}
public void setUser (String u) {this.xuser = u;}
public boolean set (String file, boolean eXist) {
try {
SAXBuilder builder = new SAXBuilder();
this.PID = "";
this.viewer = null;
if (!eXist) {
this.file = new File (file);
this.collection="";
if (this.file.exists()) {
this.mets = builder.build( this.file );
}
return this.file.exists();
} else {
eXist eX = new eXist(file);
org.xmldb.api.base.Collection collection = DatabaseManager.getCollection( URI + eX.getCollection(), user.getExistUser(), user.getExistPasswd() );
XMLResource res = (XMLResource) collection.getResource(eX.getStream());
this.mets = builder.build( new StringReader( (String) res.getContent()));
collection.close();
this.collection = file;
return true;
}
} catch (Exception e) {
log.error(e.getLocalizedMessage(),e);
return false;
}
}
public boolean set (String stream) {
try {
SAXBuilder builder = new SAXBuilder();
this.viewer = null;
this.collection="";
this.mets =builder.build(new StringReader(stream));
this.PID = "";
return true;
} catch (Exception e) {
log.error(e.getLocalizedMessage(),e);
return false;
}
}
public String toString() {
return (this.outputter.outputString(this.mets).replaceAll("this:PID", this.PID).replaceAll("this:FEDORA", user.getUrl()));
}
public String getName() {
try {
return !this.collection.isEmpty() ? this.collection : this.file.getCanonicalPath();
} catch (Exception e) {
log.error(e.getLocalizedMessage(),e);
return "";
}
}
public String getPID() {
try {
XPath xpath = XPath.newInstance( "//mods:mods/mods:identifier[@type='urn']" );
xpath.addNamespace( Common.xmlns_mods );
Element idno = (Element) xpath.selectSingleNode( mets );
if (idno != null) {
String s = idno.getTextNormalize();
this.PID = s.startsWith(Common.INFO_FEDORA) ? s.substring(Common.INFO_FEDORA.length()) : s;
}
} catch (Exception e) {
log.error(e.getLocalizedMessage(),e);
}
return this.PID;
}
public void setPID(String pid) {
try {
XPath xpath = XPath.newInstance( "//mods:mods/mods:identifier[@type='urn']" );
xpath.addNamespace( Common.xmlns_mods );
Element idno = (Element) xpath.selectSingleNode( mets );
if (idno == null) {
idno = new Element ("identifier", Common.xmlns_mods );
idno.setAttribute("type", "urn");
idno.setText(Common.INFO_FEDORA+pid);
xpath = XPath.newInstance("//mods:mods");
xpath.addNamespace( Common.xmlns_mods );
Element anchor = (Element) xpath.selectSingleNode( mets );
anchor.addContent(idno);
this.PID = pid;
} else {
if (this.mode) {
idno.setText(pid);
this.PID = pid;
}
}
} catch (Exception e) {
log.error(e.getLocalizedMessage(),e);
}
}
public boolean isValid() {
try {
XPath xpath = XPath.newInstance( "/");
xpath.addNamespace( Common.xmlns_viewer );
if ( this.mets.getRootElement().getNamespace() == Common.xmlns_viewer )
{
try {
Element structure = this.mets.getRootElement().getChild("structure", Common.xmlns_viewer );
if (structure.getChild("div", Common.xmlns_viewer) == null) {
Element div = new Element("div",Common.xmlns_viewer);
File[] images = this.file.getParentFile().listFiles(new JPGFilter());
for (int i = 0; i < images.length; i++) {
Element page = new Element("page",Common.xmlns_viewer);
page.setAttribute("href","file:///"+images[i].getAbsolutePath(), Common.xmlns_xlink);
div.addContent (page);
}
structure.addContent(div);
}
} catch (Exception s) {
log.error(s.getLocalizedMessage(),s);
}
this.viewer = this.mets;
byte[] stylesheet = null;
try {
stylesheet = Repository.getDatastream("cirilo:"+( xuser != null ? xuser : user.getUser() ), "TOMETS" , "");
} catch (Exception ex) {
stylesheet = Repository.getDatastream("cirilo:Backbone", "TOMETS" , "");
}
System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
Transformer transformer = TransformerFactory.newInstance().newTransformer(new StreamSource(new StringReader(new String(stylesheet))));
JDOMSource in = new JDOMSource(this.mets);
JDOMResult out = new JDOMResult();
transformer.transform(in, out);
System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl");
SAXBuilder builder = new SAXBuilder();
this.mets = builder.build( new StringReader( outputter.outputString(out.getResult())) );
}
xpath = XPath.newInstance( "/mets:mets" );
xpath.addNamespace( Common.xmlns_mets );
if ( xpath.selectSingleNode( this.mets ) == null) return false;
return true;
} catch (Exception e) {
log.error(e.getLocalizedMessage(),e);
return false;
}
}
public List getChildren(String path) {
try {
XPath xpath = XPath.newInstance(path);
xpath.addNamespace( Common.xmlns_mets );
List nodes = (List) xpath.selectNodes( this.mets );
return nodes;
} catch (Exception e) {
log.error(e.getLocalizedMessage(),e);
return null;
}
}
public boolean addChild(String path, String name, String text) {
try {
XPath xpath = XPath.newInstance(path);
xpath.addNamespace( Common.xmlns_mets );
Element anchor = (Element) xpath.selectSingleNode( mets );
Element child = new Element (name, Common.xmlns_mets );
child.setText(text);
anchor.addContent(child);
return true;
} catch (Exception e) {
log.error(e.getLocalizedMessage(),e);
return false;
}
}
public boolean write() {
try {
String p = props.getProperty("user", "METS.RefreshSource");
if (p != null && p.equals("1")) {
if (!onlyValidate) {
if (this.collection.isEmpty()) {
FileOutputStream fos = new FileOutputStream( this.file.toString() );
BufferedWriter out = new BufferedWriter(new OutputStreamWriter( fos, "UTF-8" ) );
out.write(outputter.outputString(this.mets).replaceAll("o:self", this.PID));
out.close();
} else {
eXist eX = new eXist (this.collection);
org.xmldb.api.base.Collection coll = DatabaseManager.getCollection( URI + eX.getCollection(), user.getExistUser(), user.getExistPasswd() );
XMLResource res = (XMLResource) coll.getResource(eX.getStream());
res.setContent(new String(outputter.outputString(this.mets).replaceAll("o:self", this.PID).getBytes("UTF-8")));
coll.storeResource(res);
coll.close();
}
}
}
if (this.viewer != null && this.viewer.getRootElement().getNamespace() == Common.xmlns_viewer ) {
Element idno = this.viewer.getRootElement().getChild("idno", Common.xmlns_viewer );
if (idno!= null) {
idno.setText(this.PID);
} else {
idno = new Element("idno", Common.xmlns_viewer );
idno.setText(this.PID);
this.viewer.getRootElement().addContent(2,idno);
}
FileOutputStream fos = new FileOutputStream( this.file.toString() );
BufferedWriter os = new BufferedWriter(new OutputStreamWriter( fos, "UTF-8" ) );
os.write(outputter.outputString(this.viewer));
os.close();
}
return true;
} catch (Exception e) {
log.error(e.getLocalizedMessage(),e);
return false;
}
}
public void ingestImages() {
try {
Element fileGrp = null;
List images = getChildren("//mets:fileGrp[@USE='DEFAULT']/mets:file");
List thumbs = getChildren("//mets:fileGrp[@USE='THUMBS']/mets:file");
String host = "http://"+ props.getProperty("user", "fedora.server") + "/";
if (images.size() > 0) {
int i = 0;
IIIFFactory i3f = (IIIFFactory) CServiceProvider.getService(ServiceNames.I3F_SERVICE);
i3f.delete(this.PID);
if (thumbs.size() == 0) {
fileGrp = new Element("fileGrp", Common.xmlns_mets);
fileGrp.setAttribute("USE", "THUMBS");
}
for (Iterator iter = images.iterator(); iter.hasNext();) {
try {
Element e = (Element) iter.next();
String id = e.getAttributeValue("ID");
i++;
id = id == null ? "IMG."+new Integer(i).toString() : id;
String mimetype = e.getAttributeValue("MIMETYPE");
Element ch = e.getChild("FLocat", Common.xmlns_mets);
if (ch.getAttributeValue("LOCTYPE").equals("URL")) {
File f = null;
String url = ch.getAttributeValue("href", Common.xmlns_xlink);
if (mimetype == null) {
if ( url.indexOf(".tif") > -1 )
mimetype ="image/tiff";
else
mimetype ="image/jpeg";
}
if (url.indexOf("http://") == -1) {
if (url.startsWith(Common.INFO_FEDORA)) {
String s = url.substring(Common.INFO_FEDORA.length());
String[] AS;
AS = s.split("/");
if (AS.length == 2) {
ch.setAttribute("href", host+AS[0]+"/"+AS[1], Common.xmlns_xlink);
}
} else {
if (this.collection.isEmpty()) {
url = url.startsWith("file:///") ? url.substring(8) : url;
f = new File (url);
if (!f.exists()) {
f = new File (file.getParent()+System.getProperty( "file.separator" )+url);
}
} else {
url = url.startsWith("exist:///") ? url.substring(9) : url;
eXist eX = new eXist(this.collection);
eXist eP = new eXist(url);
String suffix = "";
if (!eP.getCollection().isEmpty()) {
suffix = "/"+eP.getCollection();
url = eP.getStream();
}
org.xmldb.api.base.Collection coll = DatabaseManager.getCollection( URI + eX.getCollection()+suffix, user.getExistUser(), user.getExistPasswd() );
Resource res = (Resource) coll.getResource(url);
if (res != null) {
f= f.createTempFile("temp", ".tmp");
byte[] data = (byte[]) res.getContent();
FileOutputStream fos = new FileOutputStream(f);
fos.write(data);
fos.flush();
fos.close();
}
coll.close();
}
if (f != null && f.exists()){
BufferedImage img = ImageIO.read(f);
try {
Element fcontent= new Element("FContent",Common.xmlns_mets);
Element xmldata= new Element("xmlData",Common.xmlns_mets);
Element x= new Element("xmpmeta",Common.xmlns_xmp);
Element pixelx= new Element("PixelXDimension",Common.xmlns_exif);
Element pixely= new Element("PixelYDimension",Common.xmlns_exif);
pixelx.setText(new Integer(img.getWidth()).toString());
pixely.setText(new Integer(img.getHeight()).toString());
x.addContent(pixelx);
x.addContent(pixely);
xmldata.addContent(x);
fcontent.addContent(xmldata);
ch.getParentElement().addContent(fcontent);
} catch (Exception q) {}
if (!onlyValidate) {
if (Common.exist(this.PID)) {
if (!Repository.exists(this.PID, id)) {
Repository.addDatastream(this.PID, id, "Facsimile", "M", mimetype, f);
} else {
Repository.modifyDatastream(this.PID, id, mimetype, "M", f);
}
ch.setAttribute("href", host+this.PID+"/"+id, Common.xmlns_xlink);
if ( i == 1 ) {
File thumb = File.createTempFile( "temp", ".tmp" );
ImageTools.createThumbnail( f, thumb, 100, 80, Color.lightGray );
Repository.modifyDatastream(this.PID, "THUMBNAIL", "image/jpeg", "M", thumb);
thumb.delete();
}
}
} else {
Common.log(logger, "Objekt '"+this.PID+"' konnte nicht gefunden werden\n");
return;
}
try {
if (!this.collection.isEmpty()) f.delete();
} catch (Exception q) {}
} else {
Common.log(logger, "Bilddatei '"+url+"' für Objekt '"+this.PID+"' konnte nicht gefunden werden\n");
}
}
}
if (thumbs.size() == 0) {
boolean temp = false;
File thumb = File.createTempFile( "temp", ".tmp" );
String tid = "TBN."+new Integer(i).toString();
if (f == null) {
f = File.createTempFile( "temp", ".tmp" );
temp = true;
byte [] image = Repository.getDatastream(this.PID, id, (String) null);
FileOutputStream fos = new FileOutputStream(f.getAbsolutePath());
fos.write(image);
fos.close();
}
if (!Repository.exists(this.PID, tid)) {
ImageTools.createThumbnail( f, thumb, 150, 150, Color.lightGray );
Repository.addDatastream(this.PID, tid, "Thumbnail", "M", mimetype, thumb);
} else {
Repository.modifyDatastream(this.PID, tid, mimetype, "M", thumb);
}
Element file = new Element("file",Common.xmlns_mets);
file.setAttribute("ID", tid);
file.setAttribute("MIMETYPE","image/jpeg");
Element flocat = new Element("FLocat",Common.xmlns_mets);
flocat.setAttribute("LOCTYPE", "URL");
flocat.setAttribute("href", host+this.PID+"/"+tid, Common.xmlns_xlink);
file.addContent(flocat);
fileGrp.addContent(file);
XPath xpath = XPath.newInstance( "//mets:structMap[@TYPE='PHYSICAL']/mets:div[@ID='PHY.1']/mets:div[mets:fptr/@FILEID='"+id+"']" );
xpath.addNamespace( Common.xmlns_mets );
Element div = (Element) xpath.selectSingleNode( mets );
Element fptr = new Element("fptr", Common.xmlns_mets);
fptr.setAttribute("FILEID", tid);
div.addContent(fptr);
if (temp) f.delete();
thumb.delete();
}
}
} catch (Exception eq) {
log.error(eq.getLocalizedMessage(),eq);
Common.log(logger, eq); }
}
if (thumbs.size() == 0) {
XPath xpath = XPath.newInstance( "//mets:fileSec" );
xpath.addNamespace( Common.xmlns_mets );
Element fileSec = (Element) xpath.selectSingleNode( mets );
fileSec.addContent(fileGrp);
}
}
if (thumbs.size() > 0) {
int i = 0;
for (Iterator iter = thumbs.iterator(); iter.hasNext();) {
try {
Element e = (Element) iter.next();
String id = e.getAttributeValue("ID");
id = id == null ? "TBN."+new Integer(++i).toString() : id;
String mimetype = e.getAttributeValue("MIMETYPE");
mimetype = mimetype == null ? "image/jpeg" : mimetype;
Element ch = e.getChild("FLocat", Common.xmlns_mets);
if (ch.getAttributeValue("LOCTYPE").equals("URL")) {
String url = ch.getAttributeValue("href", Common.xmlns_xlink);
if (url.indexOf("http://") == -1) {
File f = null;
if (url.startsWith(Common.INFO_FEDORA)) {
String s = url.substring(Common.INFO_FEDORA.length());
String[] AS;
AS = s.split("/");
if (AS.length == 2) {
ch.setAttribute("href", host+AS[0]+"/"+AS[1], Common.xmlns_xlink);
}
} else {
if (this.collection.isEmpty()) {
url = url.startsWith("file:///") ? url.substring(8) : url;
f = new File (url);
if (!f.exists()) {
f = new File (file.getParent()+System.getProperty( "file.separator" )+url);
}
} else {
url = url.startsWith("exist:///") ? url.substring(9) : url;
eXist eX = new eXist(this.collection);
eXist eP = new eXist(url);
String suffix = "";
if (!eP.getCollection().isEmpty()) {
suffix = "/"+eP.getCollection();
url = eP.getStream();
}
org.xmldb.api.base.Collection coll = DatabaseManager.getCollection( URI + eX.getCollection()+suffix, user.getExistUser(), user.getExistPasswd() );
Resource res = (Resource) coll.getResource(url);
if (res != null) {
f= f.createTempFile("temp", ".tmp");
byte[] data = (byte[]) res.getContent();
FileOutputStream fos = new FileOutputStream(f);
fos.write(data);
fos.flush();
fos.close();
}
coll.close();
}
if (f != null && f.exists()){
if (!onlyValidate) {
if (Common.exist(this.PID)) {
i++;
File thumb = File.createTempFile( "temp", ".tmp" );
if (!Repository.exists(this.PID, id)) {
if( e.getAttribute("resize") != null) {
ImageTools.createThumbnail( f, thumb, 150, 150, Color.lightGray );
Repository.addDatastream(this.PID, id, "Thumbnail", "M", mimetype, thumb);
} else {
Repository.addDatastream(this.PID, id, "Thumbnail", "M", mimetype, f);
}
} else {
Repository.modifyDatastream(this.PID, id, mimetype, "M", f);
}
thumb.delete();
ch.setAttribute("href", host+this.PID+"/"+id, Common.xmlns_xlink);
}
} else {
Common.log(logger, "Objekt '"+this.PID+"' konnte nicht gefunden werden\n");
return;
}
try {
if (!this.collection.isEmpty()) f.delete();
} catch (Exception q) {}
continue;
} else {
Common.log(logger, "Thumbnail '"+url+"' für Objekt '"+this.PID+"' konnte nicht gefunden werden\n");
}
}
}
}
} catch (Exception eq) {
log.error(eq.getLocalizedMessage(),eq);
Common.log(logger, eq);
}
}
}
List divs = getChildren("//mets:div[@TYPE='page']");
if (divs.size() > 0) {
for (Iterator iter = divs.iterator(); iter.hasNext();) {
try {
Element e = (Element) iter.next();
Element fptr = e.getChild("fptr", Common.xmlns_mets);
String ID = fptr.getAttributeValue("FILEID");
XPath xpath = XPath.newInstance("//mets:file[@ID='"+ID+"']");
xpath.addNamespace( Common.xmlns_mets );
Element file = (Element) xpath.selectSingleNode(this.mets);
Element flocat = file.getChild("FLocat", Common.xmlns_mets);
String href = flocat.getAttributeValue("href",Common.xmlns_xlink);
if (e.getAttributeValue("CONTENTIDS") == null) e.setAttribute("CONTENTIDS", href);
} catch (Exception eq) {
log.error(eq.getLocalizedMessage(),eq);
}
}
}
} catch (Exception e) {
log.error(e.getLocalizedMessage(),e);
}
}
public void createMapping(String pid, CDefaultGuiAdapter moGA)
{
try {
if (!onlyValidate){
byte[] url = Repository.getDatastream(pid != null ? pid : this.PID, "DC_MAPPING" , "");
SAXBuilder builder = new SAXBuilder();
URLConnection con = new URL (new String(url)).openConnection();
con.setUseCaches(false);
Document mapping = builder.build(con.getInputStream());
MDMapper m = new MDMapper(this.PID,outputter.outputString(mapping));
org.jdom.Document dc = builder.build( new StringReader (m.transform(this.mets) ) );
if (moGA != null) {
Element root = dc.getRootElement();
for (int i = 1; i < org.emile.cirilo.Common.DCMI.length; i++) {
String s = (String) moGA.getText("jtf"+ org.emile.cirilo.Common.DCMI[i]);
if (s.length() > 0) {
StringTokenizer st = new StringTokenizer(s, "~");
if (st.hasMoreTokens()) {
while (st.hasMoreTokens()) {
String p = Common.itrim(st.nextToken());
Element e = new Element(
org.emile.cirilo.Common.DCMI[i].toLowerCase(),
org.emile.cirilo.Common.xmlns_dc);
e.addContent(p);
root.addContent(e);
}
}
}
}
}
dc = Common.validate(dc);
Repository.modifyDatastreamByValue(this.PID, "DC", "text/xml", outputter.outputString(dc));
url = Repository.getDatastream(pid != null ? pid : this.PID, "BIBTEX_MAPPING" , "");
con = new URL (new String(url)).openConnection();
con.setUseCaches(false);
mapping = builder.build(con.getInputStream());
m = new MDMapper(this.PID,outputter.outputString(mapping));
Repository.modifyDatastreamByValue(this.PID, "BIBTEX", "text/xml", m.transform( this.mets));
}
} catch (Exception e) {
log.error(e.getLocalizedMessage(),e);
}
}
class JPGFilter implements FilenameFilter
{
public boolean accept( File f, String s )
{
s=s.toLowerCase();
return (s.endsWith( ".jp2" ) || s.endsWith( ".jpg" ) || s.endsWith( ".jpeg" ) || s.endsWith( ".tif" ) || s.endsWith( ".tiff" )) && !s.startsWith(".");
}
}
}