/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package fedora.server.access;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import fedora.common.Constants;
import fedora.server.Context;
import fedora.server.Server;
import fedora.server.errors.GeneralException;
import fedora.server.errors.InitializationException;
import fedora.server.errors.QueryParseException;
import fedora.server.errors.ServerException;
import fedora.server.search.Condition;
import fedora.server.search.FieldSearchQuery;
import fedora.server.search.FieldSearchResult;
import fedora.server.search.ObjectFields;
import fedora.server.utilities.StreamUtility;
import fedora.utilities.XmlTransformUtility;
/**
* Implements reporting functionality.
*
* @author Bill Niebel
*/
public class Report {
/** Instance of the Server */
private static Server s_server = null;
/** Instance of the access subsystem */
private static Access s_access = null;
private static final String getFieldValue(ObjectFields f, String name) {
SimpleDateFormat formatter =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
String value = null;
if ("pid".equalsIgnoreCase(name)) {
value = f.getPid();
} else if ("label".equalsIgnoreCase(name)) {
value = f.getLabel();
} else if ("state".equalsIgnoreCase(name)) {
value = f.getState();
} else if ("ownerId".equalsIgnoreCase(name)) {
value = f.getOwnerId();
} else if ("cDate".equalsIgnoreCase(name)) {
value = formatter.format(f.getCDate());
} else if ("mDate".equalsIgnoreCase(name)) {
value = formatter.format(f.getMDate());
} else if ("dcmDate".equalsIgnoreCase(name)) {
value = formatter.format(f.getDCMDate());
}
return value;
}
private static final List getFieldValues(ObjectFields f, String name) {
List values = null;
if ("title".equalsIgnoreCase(name)) {
values = f.titles();
} else if ("creator".equalsIgnoreCase(name)) {
values = f.creators();
} else if ("subject".equalsIgnoreCase(name)) {
values = f.subjects();
} else if ("description".equalsIgnoreCase(name)) {
values = f.descriptions();
} else if ("publisher".equalsIgnoreCase(name)) {
values = f.publishers();
} else if ("contributor".equalsIgnoreCase(name)) {
values = f.contributors();
} else if ("date".equalsIgnoreCase(name)) {
values = f.dates();
} else if ("type".equalsIgnoreCase(name)) {
values = f.types();
} else if ("format".equalsIgnoreCase(name)) {
values = f.formats();
} else if ("identifier".equalsIgnoreCase(name)) {
values = f.identifiers();
} else if ("source".equalsIgnoreCase(name)) {
values = f.sources();
} else if ("language".equalsIgnoreCase(name)) {
values = f.languages();
} else if ("relation".equalsIgnoreCase(name)) {
values = f.relations();
} else if ("coverage".equalsIgnoreCase(name)) {
values = f.coverages();
} else if ("rights".equalsIgnoreCase(name)) {
values = f.rights();
}
return values;
}
private static final HashSet<String> multivalued = new HashSet<String>();
static {
String[] temp =
{"title", "creator", "subject", "description",
"publisher", "contributor", "date", "type", "format",
"identifier", "source", "language", "relation",
"coverage", "rights"};
for (String element : temp) {
multivalued.add(element);
}
}
private static final boolean isMultivalued(String name) {
return multivalued.contains(name);
}
protected static final HashSet<String> allFields =
new HashSet<String>(multivalued);
static {
String[] temp =
{"pid", "label", "state", "ownerId", "cDate", "mDate",
"dcmDate"};
for (String element : temp) {
allFields.add(element);
}
}
protected static final HashSet<String> parms = new HashSet<String>();
static {
String[] temp = {"sessionToken", "maxResults", "newBase"};
for (String element : temp) {
parms.add(element);
}
}
private static final HashSet<String> needsEnc = new HashSet<String>();
static {
String[] temp = {"label", "cModel"};
for (String element : temp) {
needsEnc.add(element);
}
}
private static final int MAXRESULTS = Integer.MAX_VALUE;
private String[] fieldsArray = null;
private String query = null;
private int maxResults = MAXRESULTS;
private String sessionToken = null;
private String reportName = null;
private String xslt = null;
private int newBase = 0;
private String prefix = null;
private String dateRange = null;
private FieldSearchResult fsr = null;
private static final String OBJECTS = "all objects";
private static final String ACTIVEOBJECTS = "active objects";
private static final String INACTIVEOBJECTS = "inactive objects";
protected static final String HTMLFORM = "htmlform";
private static final String FORM_TITLE = "Repository Reports";
private static final String FORM_SUBTITLE = "select a report";
private static final String REPORT_TITLE = "Report on Repository";
private static final HashSet<String> reportNames = new HashSet<String>();
static {
final String[] temp =
{HTMLFORM, OBJECTS, ACTIVEOBJECTS, INACTIVEOBJECTS};
for (String element : temp) {
reportNames.add(element);
}
}
private static final boolean isPredefinedReport(String name) {
return reportNames.contains(name);
}
private static final String OBJECTSQUERY = "";
private static final String[] OBJECTSFIELDSARRAY =
{"mDate", "pid", "label", "state"};
private static final String ACTIVEOBJECTSQUERY = "state='A'";
private static final String[] ACTIVEOBJECTSFIELDSARRAY =
{"mDate", "pid", "label"};
private static final String INACTIVEOBJECTSQUERY = "state='I'";
private static final String[] INACTIVEOBJECTSFIELDSARRAY =
{"mDate", "pid", "label"};
private static final Hashtable<String, String> queries =
new Hashtable<String, String>();
static {
queries.put(OBJECTS, OBJECTSQUERY);
queries.put(ACTIVEOBJECTS, ACTIVEOBJECTSQUERY);
queries.put(INACTIVEOBJECTS, INACTIVEOBJECTSQUERY);
}
private static final String getQuery(String name) {
return queries.get(name);
}
private static final Hashtable<String, String[]> fieldArrays =
new Hashtable<String, String[]>();
static {
fieldArrays.put(OBJECTS, OBJECTSFIELDSARRAY);
fieldArrays.put(ACTIVEOBJECTS, ACTIVEOBJECTSFIELDSARRAY);
fieldArrays.put(INACTIVEOBJECTS, INACTIVEOBJECTSFIELDSARRAY);
}
private static final String[] getFieldsArray(String name) {
return fieldArrays.get(name);
}
private static final int UNKNOWN = 0;
private static final int HTMLFORM_ONLY = 1;
private static final int PREDEFINED_REPORT = 2;
private static final int ADHOC_REPORT = 3;
private static final int CONTINUED_REPORT = 4;
private static final String NONE = "none";
private static final String CREATED_LT_24_HRS_AGO = "cltd";
private static final String MODIFIED_LT_24_HRS_AGO = "mltd";
private static final String CREATED_GT_24_HRS_AGO = "cgtd";
private static final String MODIFIED_GT_24_HRS_AGO = "mgtd";
private static final String CREATED_LT_1_WK_AGO = "cltw";
private static final String MODIFIED_LT_1_WK_AGO = "mltw";
private static final String CREATED_GT_1_WK_AGO = "cgtw";
private static final String MODIFIED_GT_1_WK_AGO = "mgtw";
private static final String CREATED_LT_1_MO_AGO = "cltm";
private static final String MODIFIED_LT_1_MO_AGO = "mltm";
private static final String CREATED_GT_1_MO_AGO = "cgtm";
private static final String MODIFIED_GT_1_MO_AGO = "mgtm";
private static final String CREATED_LT_1_YR_AGO = "clty";
private static final String MODIFIED_LT_1_YR_AGO = "mlty";
private static final String CREATED_GT_1_YR_AGO = "cgty";
private static final String MODIFIED_GT_1_YR_AGO = "mgty";
private static final Hashtable<String, String> dateRangeLabels;
static {
Hashtable<String, String> t = new Hashtable<String, String>();
t.put(NONE, "(regardless of when created or last modified)");
t.put(CREATED_LT_24_HRS_AGO, "created within past 24 hours");
t.put(MODIFIED_LT_24_HRS_AGO, "last modified within past 24 hours");
t.put(CREATED_GT_24_HRS_AGO, "created more than 24 hours ago");
t.put(MODIFIED_GT_24_HRS_AGO, "last modified more than 24 hours ago");
t.put(CREATED_LT_1_WK_AGO, "created within past 7 days");
t.put(MODIFIED_LT_1_WK_AGO, "last modified within past 7 days");
t.put(CREATED_GT_1_WK_AGO, "created more than 7 days ago");
t.put(MODIFIED_GT_1_WK_AGO, "last modified more than 7 days ago");
t.put(CREATED_LT_1_MO_AGO, "created within past 30 days");
t.put(MODIFIED_LT_1_MO_AGO, "last modified within past 30 days");
t.put(CREATED_GT_1_MO_AGO, "created more than 30 days ago");
t.put(MODIFIED_GT_1_MO_AGO, "last modified more than 30 days ago");
t.put(CREATED_LT_1_YR_AGO, "created within past 1 year");
t.put(MODIFIED_LT_1_YR_AGO, "last modified within past 1 year");
t.put(CREATED_GT_1_YR_AGO, "created more than 1 year ago");
t.put(MODIFIED_GT_1_YR_AGO, "last modified more than 1 year ago");
dateRangeLabels = t;
}
int requestType = UNKNOWN;
private static final long MILLISECS_IN_DAY = 1000 * 60 * 60 * 24;
private Report(Context context,
String _reportName,
String _xslt,
String[] _fieldsArray,
String _query,
String _remoteAddr,
String _maxResults,
String _sessionToken,
String _newBase,
String _prefix,
String _dateRange)
throws QueryParseException, ServerException {
try {
s_server = Server.getInstance(new File(Constants.FEDORA_HOME));
s_access =
(Access) s_server.getModule("fedora.server.access.Access");
} catch (InitializationException ie) {
throw new GeneralException("Error getting Fedora Server instance: "
+ ie.getMessage());
}
reportName = _reportName;
if (_sessionToken != null) {
if (_newBase == null) {
throw new GeneralException("new base missing");
}
sessionToken = _sessionToken;
try {
fieldsArray = getFieldsArray(reportName);
} catch (NullPointerException e) {
fieldsArray = _fieldsArray;
if (_fieldsArray == null) {
throw new GeneralException("fields array missing");
}
}
prefix = _prefix;
dateRange = _dateRange;
newBase = Integer.parseInt(_newBase);
requestType = CONTINUED_REPORT;
} else if (isPredefinedReport(reportName)) {
fieldsArray = getFieldsArray(reportName);
query = getQuery(reportName);
prefix = _prefix;
dateRange = _dateRange;
if (query != null && !"".equals(query)) {
query += " ";
}
if (prefix == null || "".equals(prefix)) {
query += "pid~*";
} else {
query += "pid~" + prefix + ":*";
}
if (dateRange != null && dateRange != "" && dateRange.length() != 4) {
throw new GeneralException("bad date range a " + dateRange);
}
if (dateRange != null && !"none".equals(dateRange)) {
String op = "";
String st = dateRange.substring(1, 3);
if ("lt".equals(st)) {
op = ">=";
} else if ("gt".equals(st)) {
op = "<";
} else {
throw new GeneralException("bad date range b");
}
String field = "";
char ch = dateRange.charAt(0);
switch (ch) {
case 'c':
field = "cDate";
break;
case 'm':
field = "mDate";
break;
default:
throw new GeneralException("bad date range c");
}
long days;
ch = dateRange.charAt(3);
switch (ch) {
case 'd':
days = 1;
break;
case 'w':
days = 7;
break;
case 'm':
days = 30;
break;
case 'y':
days = 365;
break;
default:
throw new GeneralException("bad date range d");
}
long compTime =
(new Date()).getTime() - days * MILLISECS_IN_DAY;
DateFormat df =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
String compString = df.format(new Date(compTime));
if (dateRange != null && !"".equals(dateRange)) {
query += " " + field + op + compString;
}
}
requestType = PREDEFINED_REPORT;
} else if ((reportName == null || "".equals(reportName))
&& _fieldsArray == null && _query == null) {
requestType = HTMLFORM_ONLY;
} else if (_fieldsArray != null && _query != null) {
requestType = ADHOC_REPORT;
fieldsArray = _fieldsArray;
query = _query;
} else {
throw new GeneralException("parameters don't agree");
}
if ("".equals(_xslt)) { // override default with no transform at all
} else if (_xslt != null) { // use stated
xslt = xslts.get(_xslt);
} else { // use default transform for this report
switch (requestType) {
case HTMLFORM_ONLY:
xslt = REQUEST_HTML_XSLT;
break;
default:
if (OBJECTS.equals(reportName)) {
xslt = HTML_XSLT;
} else {
xslt = HTML_XSLT;
}
}
}
switch (requestType) {
case PREDEFINED_REPORT:
case ADHOC_REPORT:
case CONTINUED_REPORT:
try {
if ("*".equals(_maxResults)) {
maxResults = Integer.MAX_VALUE;
} else {
maxResults = Integer.parseInt(_maxResults);
}
} catch (NullPointerException npe) {
} catch (NumberFormatException nfe) {
}
if (sessionToken != null) {
fsr = s_access.resumeFindObjects(context, sessionToken);
} else {
fsr =
s_access
.findObjects(context,
fieldsArray,
maxResults,
new FieldSearchQuery(Condition
.getConditions(query)));
}
if (fsr == null) {
throw new GeneralException("no fsr");
}
break;
}
}
protected static final Report getInstance(Context context,
String remoteAddr,
String sessionToken,
String reportName,
String xslt,
String maxResults,
String newBase,
String prefix,
String dateRange)
throws QueryParseException, ServerException {
return getInstance(context,
remoteAddr,
sessionToken,
reportName,
null,
null,
xslt,
maxResults,
newBase,
prefix,
dateRange);
}
private static final String XSLT_DIR = "access";
protected static final String REQUEST_HTML_XSLT = "reportRequestHtml.xslt";
protected static final String HTML_XSLT = "reportHtml.xslt";
protected static final String XML_XSLT = "reportXml.xslt";
private static final Hashtable<String, String> xslts =
new Hashtable<String, String>();
static {
xslts.put("REQUEST_HTML_XSLT", REQUEST_HTML_XSLT);
xslts.put("HTML_XSLT", HTML_XSLT);
xslts.put("XML_XSLT", XML_XSLT);
}
protected final String getContentType() {
String contentType = "text/xml";
if (REQUEST_HTML_XSLT.equals(xslt) || HTML_XSLT.equals(xslt)) {
contentType = "text/html";
}
return contentType;
}
protected static final Report getInstance(Context context,
String _remoteAddr,
String _sessionToken,
String _reportName,
String[] _fieldsArray,
String _query,
String _xslt,
String _maxResults,
String newBase,
String prefix,
String dateRange)
throws QueryParseException, ServerException {
Report report =
new Report(context,
_reportName,
_xslt,
_fieldsArray,
_query,
_remoteAddr,
_maxResults,
_sessionToken,
newBase,
prefix,
dateRange);
return report;
}
private static final void putOr(Hashtable<String, String> hashtable,
String key,
String value) {
if (value != null) {
hashtable.put(key, value);
}
}
protected final void writeOut(OutputStream ultOut)
throws QueryParseException, ServerException, IOException,
TransformerException { // PrintWriter
PrintWriter out = null;
Writer x = null;
if (xslt == null) {
out = new PrintWriter(new OutputStreamWriter(ultOut, "UTF-8"));
x = new DirectWriter(out);
} else {
Hashtable<String, String> params = new Hashtable<String, String>();
params.put("REQUEST-TYPE", Integer.toString(requestType));
switch (requestType) {
case HTMLFORM_ONLY:
putOr(params, "GENERAL-TITLE", FORM_TITLE);
putOr(params, "SPECIFIC-TITLE", FORM_SUBTITLE);
break;
case PREDEFINED_REPORT:
case ADHOC_REPORT:
case CONTINUED_REPORT:
String viewingStart = null;
String viewingEnd = null;
String newToken = fsr.getToken();
if (fsr.objectFieldsList().size() <= 0) {
viewingStart = "0";
viewingEnd = "0";
} else {
viewingStart = Long.toString(newBase + 1);
viewingEnd =
Long.toString(newBase
+ fsr.objectFieldsList().size());
if (newToken != null && !"".equals(newToken)) {
newBase += fsr.objectFieldsList().size();
}
}
putOr(params, "GENERAL-TITLE", REPORT_TITLE);
putOr(params, "SPECIFIC-TITLE", reportName);
putOr(params, "FIELDARRAY-LENGTH", Integer
.toString(fieldsArray.length));
putOr(params, "VIEWINGSTART", viewingStart);
putOr(params, "VIEWINGEND", viewingEnd);
putOr(params, "MAXRESULTS", Integer.toString(maxResults));
putOr(params, "REPORTNAME", reportName);
putOr(params, "SESSIONTOKEN", newToken);
putOr(params, "NEWBASE", Integer.toString(newBase));
putOr(params, "PREFIX", prefix);
putOr(params, "DATERANGE", dateRange);
if (dateRange != null && !"".equals(dateRange)) {
putOr(params,
"DATERANGELABEL",
dateRangeLabels.get(dateRange));
} else {
putOr(params, "DATERANGELABEL", "");
}
break;
}
out = new PrintWriter(new OutputStreamWriter(ultOut, "UTF-8"));
x = new InMemoryWriter(out, xslt, params);
}
StringBuffer outBuf = x.getStringBuffer();
outBuf.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
switch (requestType) {
case HTMLFORM_ONLY:
outBuf.append("<X />\n");
x.flush();
break;
case PREDEFINED_REPORT:
case ADHOC_REPORT:
case CONTINUED_REPORT:
outBuf.append("<result>\n");
outBuf.append("\t<listSession>\n");
if (fsr.getCursor() != -1) {
outBuf.append("\t\t<cursor>" + fsr.getCursor()
+ "</cursor>\n");
}
if (fsr.getToken() != null) {
outBuf
.append("\t\t<token>" + fsr.getToken()
+ "</token>\n");
}
if (fsr.getCompleteListSize() != -1) {
outBuf.append("\t\t<completeListSize>"
+ fsr.getCompleteListSize()
+ "</completeListSize>\n");
} else if (maxResults == Integer.MAX_VALUE) {
outBuf.append("\t\t<completeListSize>"
+ fsr.objectFieldsList().size()
+ "</completeListSize>\n");
}
if (fsr.getExpirationDate() != null) {
outBuf
.append("\t\t<expirationDate>"
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.format(fsr.getExpirationDate())
+ "</expirationDate>\n");
}
outBuf.append("\t</listSession>\n");
outBuf.append("\t<fieldNames>\n");
for (String element : fieldsArray) {
outBuf.append("\t\t<fieldName>" + element
+ "</fieldName>\n");
}
outBuf.append("\t</fieldNames>\n");
outBuf.append("\t<resultList>\n");
x.flush();
if ("text/html".equals(getContentType())) {
//header fields
}
List<ObjectFields> searchResults = fsr.objectFieldsList();
for (int i = 0; i < searchResults.size(); i++) {
ObjectFields f = searchResults.get(i);
outBuf.append("\t\t<objectFields>\n");
for (String name : fieldsArray) {
if (isMultivalued(name)) {
appendXML(name, getFieldValues(f, name), outBuf);
} else {
appendXML(name, getFieldValue(f, name), outBuf);
}
}
outBuf.append("\t\t</objectFields>\n");
x.flush();
}
outBuf.append("\t</resultList>");
outBuf.append("</result>");
break;
default:
throw new GeneralException("requestType out-of-bounds");
}
x.flush();
x.close();
}
private static void appendXML(String name, String value, StringBuffer out) {
if (value != null) {
out.append("\t\t\t<" + name + ">" + StreamUtility.enc(value) + "</"
+ name + ">\n");
}
}
private static void appendXML(String name, List values, StringBuffer out) {
for (int i = 0; i < values.size(); i++) {
appendXML(name, (String)values.get(i), out);
}
}
abstract class Writer {
PrintWriter out = null;
StringBuffer buffer = null;
public StringBuffer getStringBuffer() {
return buffer;
}
public void flush() {
//default, no op
}
public void close() {
buffer = null;
}
}
abstract class TransformerWriter
extends Writer {
String xslt = null;
Transformer transformer = null;
Hashtable<String, String> params = null;
protected void xform(Source source) throws TransformerException {
TransformerFactory factory = XmlTransformUtility.getTransformerFactory();
transformer =
factory.newTransformer(new StreamSource(s_server
.getHomeDir()
+ "/" + XSLT_DIR + "/" + xslt));
Enumeration<String> keys = params.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
String value = params.get(key);
transformer.setParameter(key, value);
}
transformer.transform(source, new StreamResult(out));
}
}
class DirectWriter
extends Writer {
PrintWriter out = null;
DirectWriter(PrintWriter out) {
this.out = out;
buffer = new StringBuffer();
}
@Override
public void flush() {
out.print(buffer.toString());
buffer.setLength(0);
}
}
class InMemoryWriter
extends TransformerWriter {
InMemoryWriter(PrintWriter out, String xslt, Hashtable<String, String> params) {
this.out = out;
this.xslt = xslt;
this.params = params;
buffer = new StringBuffer();
}
@Override
public void close() {
StringReader sr = new StringReader(buffer.toString());
StreamSource streamSource = new StreamSource(sr);
try {
xform(streamSource);
} catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
super.close();
}
}
}