/*
* Copyright 2013
*
* Licensed 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.
*/
package org.openntf.domino.design.impl;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Logger;
import org.openntf.domino.Database;
import org.openntf.domino.Document;
import org.openntf.domino.DxlImporter;
import org.openntf.domino.NoteCollection;
import org.openntf.domino.NoteCollection.SelectOption;
import org.openntf.domino.Session;
import org.openntf.domino.design.AnyFileResource;
import org.openntf.domino.design.DesignBase;
import org.openntf.domino.design.DesignBaseNamed;
import org.openntf.domino.design.DesignForm;
import org.openntf.domino.design.DesignView;
import org.openntf.domino.design.FileResource;
import org.openntf.domino.design.FileResourceHidden;
import org.openntf.domino.design.FileResourceWebContent;
import org.openntf.domino.design.XspJavaResource;
import org.openntf.domino.design.XspResource;
import org.openntf.domino.utils.DominoUtils;
import com.ibm.commons.util.StringUtil;
import com.ibm.commons.util.io.StreamUtil;
/**
* @author jgallagher
*
*/
public class DatabaseDesign implements org.openntf.domino.design.DatabaseDesign {
private static final Logger log_ = Logger.getLogger(DatabaseDesign.class.getName());
/*
* Some handy constant Note IDs for getting specific elements. h/t http://www.nsftools.com/tips/NotesTips.htm#defaultelements
*/
public static final String ABOUT_NOTE = "FFFF0002";
public static final String DEFAULT_FORM = "FFFF0004";
public static final String DEFAULT_VIEW = "FFFF0008";
public static final String ICON_NOTE = "FFFF0010";
// Design collections are pretty impossible to work with using the Java API: the exported DXL is
// blank and the raw $Collection data isn't readable as an Item
public static final String DESIGN_COLLECTION = "FFFF0020";
public static final String ACL_NOTE = "FFFF0040";
public static final String USING_NOTE = "FFFF0100";
public static final String REPLICATION_FORMULA = "FFFF0800";
private transient Properties props;
private final Database database_;
public DatabaseDesign(final Database database) {
database_ = database;
}
/* (non-Javadoc)
* @see org.openntf.domino.design.DatabaseDesign#createFolder()
*/
@Override
public Folder createFolder() {
return new Folder(database_);
}
// TODO Decide whether this should check for an existing view first. Current behavior is
// acting like it's creating a whole-cloth new view but then replacing any existing with
// the same name on save
@Override
public DesignView createView() {
return new org.openntf.domino.design.impl.DesignView(database_);
}
@Override
public FileResource createFileResource() {
return new org.openntf.domino.design.impl.FileResource(database_);
}
@Override
public StyleSheet createStyleSheet() {
return new StyleSheet(database_);
}
@Override
public AboutDocument getAboutDocument(final boolean create) {
Document doc = database_.getDocumentByID(ABOUT_NOTE);
if (doc != null) {
return new AboutDocument(doc);
} else if (create) {
return new AboutDocument(getAncestorDatabase());
// try {
// InputStream is = AboutDocument.class.getResourceAsStream("/org/openntf/domino/design/impl/dxl_helpaboutdocument.xml");
// String dxl = StreamUtil.readString(is);
// is.close();
// DxlImporter importer = getAncestorSession().createDxlImporter();
// importer.setDesignImportOption(DxlImporter.DesignImportOption.REPLACE_ELSE_CREATE);
// importer.setReplicaRequiredForReplaceOrUpdate(false);
// importer.importDxl(dxl, database_);
// doc = database_.getDocumentByID(ABOUT_NOTE);
// return new AboutDocument(doc);
// } catch (IOException e) {
// DominoUtils.handleException(e);
// }
}
return null;
}
@Override
public ACLNote getACL() {
return new ACLNote(database_.getDocumentByID(ACL_NOTE));
}
@Override
public DesignForm getDefaultForm() {
Document formDoc = database_.getDocumentByID(DEFAULT_FORM);
if (formDoc != null) {
return new org.openntf.domino.design.impl.DesignForm(formDoc);
}
return null;
}
@Override
public DesignView getDefaultView() {
Document viewDoc = database_.getDocumentByID(DEFAULT_VIEW);
if (viewDoc != null) {
return new org.openntf.domino.design.impl.DesignView(viewDoc);
}
return null;
}
/* (non-Javadoc)
* @see org.openntf.domino.design.DatabaseDesign#getFacesConfig()
*/
@Override
public FacesConfig getFacesConfig() {
return new org.openntf.domino.design.impl.FacesConfig(this);
}
@Override
public FileResource getFileResource(final String name) {
return getDesignElementByName(FileResource.class, name);
// if (DominoUtils.isUnid(name)) {
// Document doc = database_.getDocumentByUNID(name);
// return new org.openntf.domino.design.impl.FileResource(doc);
// } else {
// NoteCollection notes = getNoteCollection(
// String.format(" !@Contains($Flags; '~') & @Contains($Flags; 'g') & @Explode($TITLE; '|')=\"%s\" ",
// DominoUtils.escapeForFormulaString(name)), EnumSet.of(SelectOption.MISC_FORMAT));
//
// String noteId = notes.getFirstNoteID();
// if (!noteId.isEmpty()) {
// Document doc = database_.getDocumentByID(noteId);
// return new FileResource(doc);
// }
// }
// return null;
}
@Override
public DesignCollection<org.openntf.domino.design.FileResource> getFileResources() {
return getDesignElements(org.openntf.domino.design.FileResource.class);
//NoteCollection notes = getNoteCollection(" !@Contains($Flags; '~') & @Contains($Flags; 'g') ", EnumSet.of(SelectOption.MISC_FORMAT));
//return new DesignCollection<org.openntf.domino.design.FileResource>(notes, FileResource.class);
}
@Override
public FileResourceHidden getHiddenFileResource(final String name) {
return getDesignElementByName(FileResourceHidden.class, name);
}
@Override
public DesignCollection<FileResourceHidden> getHiddenFileResources() {
return getDesignElements(org.openntf.domino.design.FileResourceHidden.class);
}
/* (non-Javadoc)
* @see org.openntf.domino.design.DatabaseDesign#getImageResource(java.lang.String)
*/
@Override
public ImageResource getImageResource(final String name) {
return getDesignElementByName(ImageResource.class, name);
}
/* (non-Javadoc)
* @see org.openntf.domino.design.DatabaseDesign#getImageResources()
*/
@Override
public org.openntf.domino.design.DesignCollection<org.openntf.domino.design.ImageResource> getImageResources() {
return getDesignElements(org.openntf.domino.design.ImageResource.class);
}
/* (non-Javadoc)
* @see org.openntf.domino.design.DatabaseDesign#getStyleSheet(java.lang.String)
*/
@Override
public StyleSheet getStyleSheet(final String name) {
return getDesignElementByName(StyleSheet.class, name);
}
/* (non-Javadoc)
* @see org.openntf.domino.design.DatabaseDesign#getStyleSheets()
*/
@Override
public org.openntf.domino.design.DesignCollection<org.openntf.domino.design.StyleSheet> getStyleSheets() {
return getDesignElements(org.openntf.domino.design.StyleSheet.class);
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.design.DatabaseDesign#getJavaResource(java.lang.String)
*/
@Override
public XspJavaResource getXspJavaResource(final String name) {
return getDesignElementByName(XspJavaResource.class, name);
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.design.DatabaseDesign#getJavaResources()
*/
@Override
public DesignCollection<XspJavaResource> getXspJavaResources() {
return getDesignElements(XspJavaResource.class);
}
/* (non-Javadoc)
* @see org.openntf.domino.design.DatabaseDesign#getJavaResourceClassNames()
*/
@Override
public SortedSet<String> getJavaResourceClassNames() {
// TODO Decide if it's worth going through the result to remove class names that don't actually
// exist in their notes. This happens when a Java class is renamed - Domino retains the old name
// in $ClassIndexItem for some reason
SortedSet<String> result = new TreeSet<String>();
DesignCollection<XspResource> resources = getDesignElements(XspResource.class);
for (XspResource resource : resources) {
result.addAll(resource.getClassNames());
}
result.remove("");
return result;
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.design.DatabaseDesign#getXPage(java.lang.String)
*/
@Override
public XPage getXPage(final String name) {
return getDesignElementByName(XPage.class, name);
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.design.DatabaseDesign#getXPages()
*/
@Override
public DesignCollection<org.openntf.domino.design.XPage> getXPages() {
return getDesignElements(org.openntf.domino.design.XPage.class);
}
@Override
public JarResource getJarResource(final String name) {
return getDesignElementByName(JarResource.class, name);
}
@Override
public DesignCollection<org.openntf.domino.design.JarResource> getJarResources() {
return getDesignElements(org.openntf.domino.design.JarResource.class);
}
@Override
public AnyFileResource getAnyFileResource(final String name) {
return getDesignElementByName(AnyFileResource.class, name);
}
@Override
public DesignForm getForm(final String name) {
return getDesignElementByName(DesignForm.class, name);
}
@Override
public DesignCollection<org.openntf.domino.design.DesignForm> getForms() {
return getDesignElements(org.openntf.domino.design.DesignForm.class);
}
@Override
public IconNote getIconNote() {
Document iconNote = database_.getDocumentByID(ICON_NOTE);
if (iconNote != null) {
return new IconNote(iconNote);
}
log_.fine("No icon note found for database " + getAncestorDatabase().getApiPath());
return null;
}
@Override
public ReplicationFormula getReplicationFormula() {
Document repNote = database_.getDocumentByID(REPLICATION_FORMULA);
if (repNote != null) {
return new ReplicationFormula(repNote);
}
return null;
}
@Override
public UsingDocument getUsingDocument(final boolean create) {
Document doc = database_.getDocumentByID(USING_NOTE);
if (doc != null) {
return new UsingDocument(doc);
} else if (create) {
try {
InputStream is = AboutDocument.class.getResourceAsStream("/org/openntf/domino/design/impl/dxl_helpusingdocument.xml");
String dxl = StreamUtil.readString(is);
is.close();
DxlImporter importer = getAncestorSession().createDxlImporter();
importer.setDesignImportOption(DxlImporter.DesignImportOption.REPLACE_ELSE_CREATE);
importer.setReplicaRequiredForReplaceOrUpdate(false);
importer.importDxl(dxl, database_);
doc = database_.getDocumentByID(USING_NOTE);
return new UsingDocument(doc);
} catch (IOException e) {
DominoUtils.handleException(e);
}
}
return null;
}
@Override
public DesignView getView(final String name) {
return getDesignElementByName(DesignView.class, name);
}
@Override
public DesignCollection<DesignView> getViews() {
return getDesignElements(org.openntf.domino.design.DesignView.class);
}
@Override
public Folder getFolder(final String name) {
return getDesignElementByName(Folder.class, name);
}
@Override
public DesignCollection<org.openntf.domino.design.Folder> getFolders() {
return getDesignElements(org.openntf.domino.design.Folder.class);
}
@Override
public DesignCollection<org.openntf.domino.design.ScriptLibraryJava> getScriptLibrariesJava() {
return getDesignElements(org.openntf.domino.design.ScriptLibraryJava.class);
}
@Override
public ScriptLibraryJava getScriptLibraryJava(final String name) {
return getDesignElementByName(ScriptLibraryJava.class, name);
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.design.DatabaseDesign#getDatabaseClassLoader()
*/
@Override
public DatabaseClassLoader getDatabaseClassLoader(final ClassLoader parent) {
return new DatabaseClassLoader(this, parent, true, false);
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.design.DatabaseDesign#getDatabaseClassLoader(java.lang.ClassLoader, boolean)
*/
@Override
public DatabaseClassLoader getDatabaseClassLoader(final ClassLoader parent, final boolean includeJars) {
return new DatabaseClassLoader(this, parent, includeJars, false);
}
@Override
public DatabaseClassLoader getDatabaseClassLoader(final ClassLoader parent, final boolean includeJars, final boolean includeLibraries) {
return new DatabaseClassLoader(this, parent, includeJars, includeLibraries);
}
// protected NoteCollection getNoteCollection(final String selectionFormula, final Set<SelectOption> options) {
// NoteCollection notes = database_.createNoteCollection(false);
// notes.setSelectOptions(options);
// notes.setSelectionFormula(selectionFormula);
// notes.buildCollection();
// return notes;
// }
/*
* (non-Javadoc)
*
* @see org.openntf.domino.types.DatabaseDescendant#getAncestorDatabase()
*/
@Override
public Database getAncestorDatabase() {
return database_;
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.types.SessionDescendant#getAncestorSession()
*/
@Override
public Session getAncestorSession() {
return database_.getAncestorSession();
}
@Override
public synchronized String[] getXspProperty(final String propertyName) {
if (props == null) {
props = new Properties();
FileResourceWebContent res = getDesignElementByName(FileResourceWebContent.class, "WEB-INF/xsp.properties");
if (res != null) {
try {
props.load(new ByteArrayInputStream(res.getFileData()));
} catch (IOException e) {
DominoUtils.handleException(e);
}
}
}
String setting = props.getProperty(propertyName);
if (StringUtil.isNotEmpty(setting)) {
if (StringUtil.indexOfIgnoreCase(setting, ",") > -1) {
return StringUtil.splitString(setting, ',');
} else {
return new String[] { setting };
}
}
return new String[0];
}
@Override
public boolean isAPIEnabled() {
for (String s : getXspProperty("xsp.library.depends")) {
if (s.equalsIgnoreCase("org.openntf.domino.xsp.XspLibrary")) {
return true;
}
}
return false;
}
@Override
public boolean isAppFlagSet(final String flagName) {
for (String s : getXspProperty("org.openntf.domino.xsp")) {
if (s.equalsIgnoreCase(flagName)) {
return true;
}
}
return false;
}
@Override
public DesignCollection<DesignBase> getDesignElements(final String formula) {
NoteCollection notes = database_.createNoteCollection(false);
notes.setSelectOptions(EnumSet.of(SelectOption.ALL_BUT_NOT, SelectOption.DOCUMENTS, SelectOption.PROFILES));
notes.setSelectionFormula(formula);
notes.buildCollection();
return new DesignCollection<DesignBase>(notes, null);
}
@Override
public <T extends DesignBase> DesignCollection<T> getDesignElements(final Class<T> type) {
return getDesignElements(type, null);
}
@Override
public <T extends DesignBase> DesignCollection<T> getDesignElements(final Class<T> type, final String search) {
return DesignFactory.search(database_, type, search);
}
@Override
public <T extends DesignBase> DesignCollection<T> getDesignElementsByName(final Class<T> type, final String name) {
return DesignFactory.search(database_, type,
String.format("@Explode($TITLE; '|')=\"%s\" ", DominoUtils.escapeForFormulaString(name)));
}
@Override
public <T extends DesignBase> T getDesignElementByName(final Class<T> type, final String name) {
return getDesignElementByName(type, name, false);
}
@SuppressWarnings("unchecked")
public <T extends DesignBase> T getDesignElementByName(final Class<T> type, final String name, final boolean create) {
if (DominoUtils.isUnid(name)) {
Document doc = database_.getDocumentByUNID(name);
return (T) DesignFactory.fromDocument(doc);
}
Iterator<T> elems = getDesignElementsByName(type, name).iterator();
if (elems.hasNext())
return elems.next();
if (!create)
return null;
for (ODPMapping mapping : ODPMapping.values()) {
Class<? extends AbstractDesignBase> cls = mapping.getInstanceClass();
if (type.isAssignableFrom(cls)) {
try {
Constructor<? extends AbstractDesignBase> cTor = cls.getConstructor(Database.class);
AbstractDesignBase ret = cTor.newInstance(getAncestorDatabase());
if (ret instanceof DesignBaseNamed) {
((DesignBaseNamed) ret).setName(name);
}
return (T) ret;
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
throw new IllegalArgumentException("Cannot Create a DesignElement of type " + type.getName() + " with name " + name);
}
}