/*
* Copyright (C) 2010 - 2011 Interactive Media Management
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package dk.i2m.converge.ejb.facades;
import dk.i2m.converge.core.content.ContentTag;
import dk.i2m.converge.core.metadata.OpenCalaisMapping;
import dk.i2m.converge.core.security.UserAccount;
import dk.i2m.converge.core.DataNotFoundException;
import dk.i2m.converge.core.metadata.ConceptOutput;
import dk.i2m.converge.core.metadata.Subject;
import dk.i2m.converge.ejb.services.DaoServiceLocal;
import dk.i2m.converge.ejb.services.DirectoryException;
import dk.i2m.converge.ejb.services.QueryBuilder;
import dk.i2m.converge.ejb.services.UserNotFoundException;
import dk.i2m.converge.ejb.services.UserServiceLocal;
import com.getconverge.nar.newsml.g2.power.Concept;
import com.getconverge.nar.newsml.g2.power.ConceptNameType;
import com.getconverge.nar.newsml.g2.power.Definition;
import com.getconverge.nar.newsml.g2.power.KnowledgeItem;
import com.getconverge.nar.newsml.g2.power.RelatedConceptType;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.text.MessageFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import org.apache.poi.hssf.usermodel.HSSFHeader;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.usermodel.HeaderFooter;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.WorkbookUtil;
/**
* Session bean providing a facade for meta data.
*
* @author Allan Lykke Christensen
*/
@Stateless
public class MetaDataFacadeBean implements MetaDataFacadeLocal {
private static final Logger LOG = Logger.getLogger(MetaDataFacadeBean.class.
getName());
@EJB private DaoServiceLocal daoService;
@EJB private UserServiceLocal userService;
@EJB private UserFacadeLocal userFacade;
@Resource private SessionContext ctx;
/** {@inheritDoc } */
@Override
public dk.i2m.converge.core.metadata.Concept create(
dk.i2m.converge.core.metadata.Concept concept) {
Calendar now = Calendar.getInstance();
concept.setCreated(now);
concept.setUpdated(now);
concept.setCode(concept.getTypeId() + ":" + now.getTimeInMillis());
try {
UserAccount updaterUser = userService.findById(ctx.
getCallerPrincipal().getName());
concept.setUpdatedBy(updaterUser);
} catch (UserNotFoundException ex) {
// Concept was created through automated service, e.g. OpenCalais
LOG.log(Level.FINE, "Updating user is unknown {0}", ctx.
getCallerPrincipal().getName());
} catch (DirectoryException ex) {
LOG.log(Level.SEVERE, "Could not connect to directory server.", ex);
}
return daoService.create(concept);
}
/** {@inheritDoc } */
@Override
public void deleteConcept(Long id) {
daoService.delete(dk.i2m.converge.core.metadata.Concept.class, id);
}
/** {@inheritDoc } */
@Override
public void delete(Class clazz, Long id) {
daoService.delete(clazz, id);
}
/** {@inheritDoc } */
@Override
public dk.i2m.converge.core.metadata.Concept update(
dk.i2m.converge.core.metadata.Concept concept) {
Calendar now = Calendar.getInstance();
concept.setUpdated(now);
try {
UserAccount updaterUser = userService.findById(ctx.
getCallerPrincipal().getName());
concept.setUpdatedBy(updaterUser);
} catch (Exception ex) {
LOG.log(Level.SEVERE, "Unknown user", ex);
}
return daoService.update(concept);
}
/** {@inheritDoc } */
@Override
public List<dk.i2m.converge.core.metadata.Concept> getConcepts() {
return daoService.findAll(dk.i2m.converge.core.metadata.Concept.class);
}
/** {@inheritDoc } */
@Override
public List<dk.i2m.converge.core.metadata.Concept> findRecentConcepts(
int count) {
return daoService.findWithNamedQuery(
dk.i2m.converge.core.metadata.Concept.FIND_RECENTLY_ADDED, count);
}
/** {@inheritDoc } */
@Override
public dk.i2m.converge.core.metadata.Concept findConceptById(Long id) throws
DataNotFoundException {
return daoService.findById(dk.i2m.converge.core.metadata.Concept.class,
id);
}
/** {@inheritDoc } */
@Override
public dk.i2m.converge.core.metadata.Concept findConceptByName(String name)
throws DataNotFoundException {
Map params = QueryBuilder.with("name", name).parameters();
return daoService.findObjectWithNamedQuery(
dk.i2m.converge.core.metadata.Concept.class,
dk.i2m.converge.core.metadata.Concept.FIND_BY_NAME, params);
}
/** {@inheritDoc } */
@Override
public List<Subject> findSubjectsByParent(Subject parent) {
Map params = QueryBuilder.with("parent", parent).parameters();
return daoService.findWithNamedQuery(Subject.FIND_BY_PARENT, params);
}
@Override
public List<Subject> findTopLevelSubjects() {
return daoService.findWithNamedQuery(Subject.FIND_PARENT_SUBJECTS);
}
/** {@inheritDoc } */
@Override
public dk.i2m.converge.core.metadata.Concept findConceptByCode(String code)
throws DataNotFoundException {
Map params = QueryBuilder.with("code", code).parameters();
return daoService.findObjectWithNamedQuery(
dk.i2m.converge.core.metadata.Concept.class,
dk.i2m.converge.core.metadata.Concept.FIND_BY_CODE, params);
}
/** {@inheritDoc} */
@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Override
public int importKnowledgeItem(String xml, String language) {
//TODO: Allow for importing other items than Subjects
String languageVariant;
if (language.indexOf("_") == -1) {
languageVariant = language.replaceAll("-", "_");
} else {
languageVariant = language.replaceAll("_", "-");
}
try {
StringReader reader = new StringReader(xml);
int imported = 0;
JAXBContext jaxbContext = JAXBContext.newInstance(
KnowledgeItem.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
KnowledgeItem ki = (KnowledgeItem) unmarshaller.unmarshal(reader);
for (Concept c : ki.getConceptSet().getConcept()) {
Subject subject;
String subjectCode = c.getConceptId().getQcode();
String subjectTitle = "No translation for [" + language + "]";
String subjectDefinition = "No translation for [" + language
+ "]";
// Determine if the subject already exist in the database
// Note: If a subject already exist in the database it should
// be updated rather than ignored
boolean update = false;
try {
subject = (Subject) findConceptByCode(subjectCode);
update = true;
} catch (DataNotFoundException e) {
subject = new Subject();
update = false;
}
for (ConceptNameType cn : c.getName()) {
if (cn.getLang().equalsIgnoreCase(language) || cn.getLang().
equalsIgnoreCase(languageVariant)) {
subjectTitle = cn.getValue();
}
}
for (Object obj : c.getDefinitionOrNoteOrFacet()) {
if (obj instanceof Definition) {
Definition def = (Definition) obj;
if (def.getLang().equalsIgnoreCase(language) || def.
getLang().equalsIgnoreCase(languageVariant)) {
for (Object defContent : def.getContent()) {
subjectDefinition = (String) defContent;
}
}
}
}
Calendar now = Calendar.getInstance();
subject.setCreated(now);
subject.setUpdated(now);
subject.setName(subjectTitle);
subject.setDefinition(subjectDefinition);
subject.setCode(subjectCode);
boolean child = false;
String parentCode = "";
for (Object obj : c.getSameAsOrBroaderOrNarrower()) {
JAXBElement element = (JAXBElement) obj;
if (element.getName().getLocalPart().equalsIgnoreCase(
"broader")) {
RelatedConceptType related =
(RelatedConceptType) element.getValue();
child = true;
parentCode = related.getQcode();
}
}
if (child) {
try {
Subject parent = (Subject) findConceptByCode(parentCode);
subject.getBroader().add(parent);
} catch (DataNotFoundException dnfe) {
LOG.log(Level.WARNING,
"Specify broader concept (parent) with qcode: {0} could not be found",
parentCode);
}
}
if (update) {
update(subject);
} else {
daoService.create(subject);
}
imported++;
}
return imported;
} catch (JAXBException ex) {
LOG.log(Level.SEVERE, "Could not import KnowledgeItem", ex);
return 0;
}
}
/** {@inheritDoc} */
@Override
public String[] getLanguagesAvailableForImport(String xml) {
List<String> availableLanguages = new ArrayList<String>();
try {
StringReader reader = new StringReader(xml);
JAXBContext jaxbContext = JAXBContext.newInstance(
KnowledgeItem.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
KnowledgeItem ki = (KnowledgeItem) unmarshaller.unmarshal(reader);
for (Concept c : ki.getConceptSet().getConcept()) {
for (ConceptNameType cn : c.getName()) {
availableLanguages.add(cn.getLang());
}
break;
}
} catch (JAXBException ex) {
LOG.log(Level.SEVERE, "Could not import KnowledgeItem", ex);
}
return availableLanguages.toArray(new String[availableLanguages.size()]);
}
/** {@inheritDoc } */
@Override
public List<dk.i2m.converge.core.metadata.Concept> search(String search) {
Map params =
QueryBuilder.with("keyword", "%" + search + "%").parameters();
return daoService.findWithNamedQuery(
dk.i2m.converge.core.metadata.Concept.FIND_BY_NAME_OR_DEFINITION,
params);
}
/** {@inheritDoc} */
@Override
public List<dk.i2m.converge.core.metadata.Concept> findConceptByType(
Class type) {
return daoService.findAll(type);
}
@Override
public List<dk.i2m.converge.core.metadata.Concept> findConceptsByName(
String conceptName, Class... types) {
Map<String, Object> params = QueryBuilder.with("name", conceptName + "%").
parameters();
List<dk.i2m.converge.core.metadata.Concept> conceptMatches = daoService.
findWithNamedQuery(
dk.i2m.converge.core.metadata.Concept.FIND_BY_LIKE_NAME, params);
List<dk.i2m.converge.core.metadata.Concept> matches =
new ArrayList<dk.i2m.converge.core.metadata.Concept>();
for (dk.i2m.converge.core.metadata.Concept c : conceptMatches) {
for (Class type : types) {
if (c.getClass().equals(type)) {
matches.add(c);
}
}
}
return matches;
}
@Override
public ContentTag findOrCreateContentTag(String name) {
Map<String, Object> params =
QueryBuilder.with("name", name).parameters();
ContentTag tag;
try {
tag = daoService.findObjectWithNamedQuery(ContentTag.class,
ContentTag.FIND_BY_NAME, params);
} catch (DataNotFoundException ex) {
tag = daoService.create(new ContentTag(name));
}
return tag;
}
@Override
public List<ContentTag> findContentTagLikeName(String name) {
Map<String, Object> params = QueryBuilder.with("name", "%" + name + "%").
parameters();
return daoService.findWithNamedQuery(ContentTag.FIND_LIKE_NAME, params);
}
/**
* Gets all the mappings between Open Calais and Concepts.
* <p/>
* @return {@link List} of mappings between Open Calais and Concepts
*/
@Override
public List<OpenCalaisMapping> getOpenCalaisMappings() {
return daoService.findAll(OpenCalaisMapping.class);
}
@Override
public OpenCalaisMapping create(OpenCalaisMapping mapping) {
return daoService.create(mapping);
}
@Override
public OpenCalaisMapping update(OpenCalaisMapping mapping) {
return daoService.update(mapping);
}
@Override
public void deleteOpenCalaisMapping(Long id) {
daoService.delete(OpenCalaisMapping.class, id);
}
@Override
public dk.i2m.converge.core.metadata.Concept findOpenCalaisMapping(
String typeGroup, String field, String value) throws
DataNotFoundException {
Map<String, Object> params = QueryBuilder.with("typeGroup", typeGroup).
and("field", field).and("value", value).parameters();
List<OpenCalaisMapping> mappings =
daoService.findWithNamedQuery(
OpenCalaisMapping.FIND_BY_TYPE_GROUP_FIELD_AND_VALUE, params);
if (mappings.isEmpty()) {
throw new DataNotFoundException("No mapping for " + typeGroup
+ " with field " + field + " equal to " + value);
}
OpenCalaisMapping firstMatch = mappings.iterator().next();
return firstMatch.getConcept();
}
/** {@inheritDoc } */
@Override
public byte[] exportConcepts(Class clazz, ConceptOutput format) {
// TODO: Differentiate between different output
// TODO: Differentiate between Concept types
final String I18N_PREFIX = "MetaDataFacadeBean_exportConcepts_";
ResourceBundle i18n;
try {
String uid = ctx.getCallerPrincipal().getName();
UserAccount user = userFacade.findById(uid);
Locale userLocale = user.getPreferredLocale();
i18n = ResourceBundle.getBundle(
"dk.i2m.converge.i18n.ServiceMessages", userLocale);
} catch (DataNotFoundException ex) {
i18n = ResourceBundle.getBundle(
"dk.i2m.converge.i18n.ServiceMessages");
}
String lblSheetName = i18n.getString(I18N_PREFIX + "SHEET_NAME");
String lblHeaderLeft = i18n.getString(I18N_PREFIX + "HEADER_LEFT");
String lblHeaderRight = i18n.getString(I18N_PREFIX + "HEADER_RIGHT");
String lblFooterLeft = i18n.getString(I18N_PREFIX + "FOOTER_LEFT");
String lblFooterRight = i18n.getString(I18N_PREFIX + "FOOTER_RIGHT");
String lblDateFormat = i18n.getString(I18N_PREFIX + "DATE_FORMAT");
String lblRowHeaderId = i18n.getString(I18N_PREFIX + "ROW_HEADER_ID");
String lblRowHeaderName =
i18n.getString(I18N_PREFIX + "ROW_HEADER_NAME");
String lblRowHeaderDefinition = i18n.getString(I18N_PREFIX
+ "ROW_HEADER_DEFINITION");
HSSFWorkbook wb = new HSSFWorkbook();
String sheetName = WorkbookUtil.createSafeSheetName(lblSheetName);
Font storyFont = wb.createFont();
storyFont.setFontHeightInPoints((short) 12);
storyFont.setBoldweight(Font.BOLDWEIGHT_NORMAL);
// Create style with borders
CellStyle style = wb.createCellStyle();
style.setBorderBottom(CellStyle.BORDER_THIN);
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderLeft(CellStyle.BORDER_THIN);
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderRight(CellStyle.BORDER_THIN);
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderTop(CellStyle.BORDER_THIN);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
// Create style for date cells
CreationHelper createHelper = wb.getCreationHelper();
CellStyle dateStyle = wb.createCellStyle();
dateStyle.setDataFormat(createHelper.createDataFormat().getFormat(
lblDateFormat));
dateStyle.setBorderBottom(CellStyle.BORDER_THIN);
dateStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
dateStyle.setBorderLeft(CellStyle.BORDER_THIN);
dateStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
dateStyle.setBorderRight(CellStyle.BORDER_THIN);
dateStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
dateStyle.setBorderTop(CellStyle.BORDER_THIN);
dateStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
HSSFSheet overviewSheet = wb.createSheet(sheetName);
// Create sheet header
HSSFHeader sheetHeader = overviewSheet.getHeader();
sheetHeader.setLeft(lblHeaderLeft);
sheetHeader.setRight(lblHeaderRight);
// Create sheet footer
Footer footer = overviewSheet.getFooter();
String footerLeft = MessageFormat.format(lblFooterLeft,
new Object[]{HeaderFooter.page(), HeaderFooter.numPages()});
String footerRight = MessageFormat.format(lblFooterRight,
new Object[]{HeaderFooter.date(), HeaderFooter.time()});
footer.setLeft(footerLeft);
footer.setRight(footerRight);
Row row = overviewSheet.createRow(0);
row.createCell(0).setCellValue(""); // Id
row.getCell(0).setCellStyle(style);
row.createCell(1).setCellValue(lblRowHeaderName);
row.getCell(1).setCellStyle(style);
row.createCell(2).setCellValue(lblRowHeaderDefinition);
row.getCell(2).setCellStyle(style);
// Freeze the header row
overviewSheet.createFreezePane(0, 1, 0, 1);
int overviewSheetRow = 0;
List<dk.i2m.converge.core.metadata.Concept> concepts =
findConceptByType(clazz);
for (dk.i2m.converge.core.metadata.Concept concept : concepts) {
row = overviewSheet.createRow(overviewSheetRow++);
row.createCell(0).setCellValue(concept.getId());
row.createCell(1).setCellValue(concept.getFullTitle());
row.createCell(2).setCellValue(concept.getDefinition());
}
// Auto-size
for (int i = 0; i <= 2; i++) {
overviewSheet.autoSizeColumn(i);
}
wb.setRepeatingRowsAndColumns(0, 0, 0, 0, 0);
overviewSheet.setFitToPage(true);
overviewSheet.setAutobreaks(true);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
wb.write(baos);
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
}
return baos.toByteArray();
}
private void generateChildRows(HSSFSheet overviewSheet, CellStyle style,
dk.i2m.converge.core.metadata.Concept concept, int rowNumber,
int indent) {
if (concept == null) {
return;
}
int col = 0;
HSSFRow row = overviewSheet.createRow(rowNumber++);
for (col = 0; col <= indent; col++) {
row.createCell(col).setCellValue(" ");
row.getCell(col).setCellStyle(style);
}
row.createCell(col + 1).setCellValue(concept.getName());
row.getCell(col + 1).setCellStyle(style);
for (dk.i2m.converge.core.metadata.Concept c : concept.getNarrower()) {
generateChildRows(overviewSheet, style, c, rowNumber++, indent + 1);
}
}
}