All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hcl.domino.jna.dbdirectory.JNADirectorySearchQuery Maven / Gradle / Ivy

The newest version!
/*
 * ==========================================================================
 * Copyright (C) 2019-2022 HCL America, Inc. ( http://www.hcl.com/ )
 *                            All rights reserved.
 * ==========================================================================
 * 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 .
 *
 * 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 com.hcl.domino.jna.dbdirectory;

import java.lang.ref.ReferenceQueue;
import java.text.MessageFormat;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.stream.Stream;

import com.hcl.domino.DominoException;
import com.hcl.domino.commons.gc.APIObjectAllocations;
import com.hcl.domino.commons.gc.IAPIObject;
import com.hcl.domino.commons.gc.IGCDominoClient;
import com.hcl.domino.commons.util.StringTokenizerExt;
import com.hcl.domino.commons.views.IItemTableData;
import com.hcl.domino.data.Database.Action;
import com.hcl.domino.data.DominoDateTime;
import com.hcl.domino.dbdirectory.DatabaseData;
import com.hcl.domino.dbdirectory.DirEntry;
import com.hcl.domino.dbdirectory.DirectorySearchQuery;
import com.hcl.domino.dbdirectory.FileType;
import com.hcl.domino.dbdirectory.FolderData;
import com.hcl.domino.exception.FileDoesNotExistException;
import com.hcl.domino.exception.InvalidRemotePathnameException;
import com.hcl.domino.jna.BaseJNAAPIObject;
import com.hcl.domino.jna.data.JNADatabase;
import com.hcl.domino.jna.data.JNADominoDateTime;
import com.hcl.domino.jna.internal.gc.allocations.JNADirectorySearchQueryAllocations;
import com.hcl.domino.jna.internal.search.NotesSearch;
import com.hcl.domino.jna.internal.search.NotesSearch.JNASearchMatch;
import com.hcl.domino.misc.Loop;

/**
 * Implementation of a directory search query for the Domino data directories.
 *
 * @author Tammo Riedinger
 */
public class JNADirectorySearchQuery extends BaseJNAAPIObject
    implements DirectorySearchQuery {
  private String m_server;
  private String m_directory;
  private String m_formula;
  private EnumSet m_searchFlags = EnumSet.of(SearchFlag.FILETYPE, SearchFlag.SUMMARY);
  private EnumSet m_fileTypes = EnumSet.of(FileType.DIRS);
  private JNADominoDateTime m_since;

  public JNADirectorySearchQuery(IAPIObject parent) {
    super(parent);
  }

  @SuppressWarnings("rawtypes")
  @Override
  protected JNADirectorySearchQueryAllocations createAllocations(IGCDominoClient parentDominoClient,
      APIObjectAllocations parentAllocations, ReferenceQueue queue) {

    return new JNADirectorySearchQueryAllocations(parentDominoClient, parentAllocations, this,
        queue);
  }

  @Override
  public DirectorySearchQuery withServer(String server) {
    m_server = server;

    return this;
  }

  @Override
  public DirectorySearchQuery withDirectory(String directory) {
    m_directory = directory;

    return this;
  }

  @Override
  public DirectorySearchQuery withFormula(String formula) {
    m_formula = formula;

    return this;
  }

  @Override
  public DirectorySearchQuery withFlags(Collection searchFlags) {
    m_searchFlags =
        searchFlags == null ? EnumSet.noneOf(SearchFlag.class) : EnumSet.copyOf(searchFlags);

    return this;
  }

  @Override
  public DirectorySearchQuery withFileTypes(Collection fileTypes) {
    m_fileTypes = fileTypes == null ? EnumSet.noneOf(FileType.class) : EnumSet.copyOf(fileTypes);

    return this;
  }

  @Override
  public DirectorySearchQuery since(TemporalAccessor since) {
    m_since = JNADominoDateTime.from(since);

    return this;
  }

  @Override
  public void forEach(int skip, int limit, BiConsumer consumer)
      throws DominoException {
    JNADatabase dir;
    try {
      dir = (JNADatabase) getParentDominoClient().openDatabase(m_server,
          (m_directory == null) ? "" : m_directory); //$NON-NLS-1$
    } catch(FileDoesNotExistException | InvalidRemotePathnameException e) {
      // This can occur with dotfile directories - consider it empty
      return;
    }

    try {
    	//make sure to enable directory mode and
    	//read the summary buffer with the details for each search result (e.g. path/title/type)
    	EnumSet searchFlagsWithSummary = EnumSet.copyOf(m_searchFlags);
    	searchFlagsWithSummary.add(SearchFlag.SUMMARY);
    	searchFlagsWithSummary.add(SearchFlag.FILETYPE);
    	
    	NotesSearch.searchFiles(dir, null, m_formula, "-", searchFlagsWithSummary, m_fileTypes, //$NON-NLS-1$
          m_since, new NotesSearch.SearchCallback() {
            LoopImpl loop = new LoopImpl();
            AtomicInteger counter = new AtomicInteger(1);

            @Override
            public Action noteFound(JNADatabase parentDb, JNASearchMatch searchMatch,
                IItemTableData summaryBufferData) {
              if (limit >= 0 && loop.getIndex() >= limit) {
                return Action.Stop;
              }

              JNADirEntry entry = toEntry(summaryBufferData);
              if (entry != null) {
                if ("..".equals(entry.getFileName())) { //$NON-NLS-1$
                  // skip ".." entry
                  return Action.Continue;
                }

                entry.setServer(m_server);

                if (counter.incrementAndGet() > skip) {

                  consumer.accept(entry, loop);

                  loop.next();
                }
                return loop.isStopped() ? Action.Stop : Action.Continue;
              } else {
                return Action.Continue;
              }
            }
          });
    } finally {
      dir.close();
    }
  }

  @Override
  public Stream stream() throws DominoException {
    // TODO consider whether it might be worthwhile to implement a custom stream
    // without reading (and hence decoding) all entries to a list first
    final ArrayList foundEntries = new ArrayList<>();

    forEach(0, -1, (t, u) -> foundEntries.add(t));

    return foundEntries.stream();
  }

  /**
   * Parses the summary-buffer to different subclasses of FileEntry.
   * 
   * @param summaryBufferData the summary data
   * @return the parsed entry or null
   */
  private JNADirEntry toEntry(IItemTableData summaryBufferData) {
	  Map dataAsMap = summaryBufferData.asMap(true);


	  JNADirEntry retEntry;

	  String typeStr;
	  Object typeObj = dataAsMap.get("$type"); //$NON-NLS-1$
	  if (!(typeObj instanceof String)) {
		  //$type is missing for any non-Domino files like .tmp
		  typeStr = ""; //$NON-NLS-1$
	  }
	  else {
		  typeStr = (String) typeObj;
	  }
	  
	  if ("$DIR".equals(typeStr)) { //$NON-NLS-1$
		  retEntry = new JNAFolderData();
		  
	  } else if ("$NOTEFILE".equals(typeStr)) { //$NON-NLS-1$
		  String dbTitle = ""; //$NON-NLS-1$
		  String dbCategory = ""; //$NON-NLS-1$
		  String dbTemplateName = ""; //$NON-NLS-1$
		  String dbInheritTemplateName = ""; //$NON-NLS-1$

		  Object infoObj = dataAsMap.get("$Info"); //$NON-NLS-1$
		  if (infoObj instanceof String) {
			  // parse weird $Info format:
			  // $info=Database title\n
			  // Database category\n
			  // #1Database template\n
			  // #2Database inherit template
			  String infoStr = ((String) infoObj).replace("\r", ""); //$NON-NLS-1$ //$NON-NLS-2$
			  StringTokenizerExt st = new StringTokenizerExt(infoStr, "\n"); //$NON-NLS-1$
			  if (st.hasMoreTokens()) {
				  dbTitle = st.nextToken();

				  boolean secondLine = true;
				  while (st.hasMoreTokens()) {
					  String currLine = st.nextToken();

					  if (secondLine) {
						  secondLine = false;

						  if (!currLine.startsWith("1#") && !currLine.startsWith("2#")) { //$NON-NLS-1$ //$NON-NLS-2$
							  dbCategory = currLine;
							  continue;
						  }
					  }

					  if (currLine.startsWith("#1")) { //$NON-NLS-1$
						  dbTemplateName = currLine.substring(2);
					  } else if (currLine.startsWith("#2")) { //$NON-NLS-1$
						  dbInheritTemplateName = currLine.substring(2);
					  }
				  }
			  }
		  }

		  DominoDateTime dbCreated = getDateValue(dataAsMap, "$DBCREATED"); //$NON-NLS-1$
		  DominoDateTime lastFixup = getDateValue(dataAsMap, "$lastfixup"); //$NON-NLS-1$
		  DominoDateTime lastCompact = getDateValue(dataAsMap, "$lastcompact"); //$NON-NLS-1$
		  DominoDateTime nonDataMod = getDateValue(dataAsMap, "$nondatamod"); //$NON-NLS-1$
		  DominoDateTime dataMod = getDateValue(dataAsMap, "$datamod"); //$NON-NLS-1$

		  JNADatabaseData dbData = new JNADatabaseData();
		  dbData.setTitle(dbTitle);
		  dbData.setCreated(dbCreated);
		  dbData.setLastFixup(lastFixup);
		  dbData.setLastCompact(lastCompact);
		  dbData.setDesignModifiedDate(nonDataMod);
		  dbData.setDataModifiedDate(dataMod);
		  dbData.setCategory(dbCategory);
		  dbData.setTemplateName(dbTemplateName);
		  dbData.setInheritTemplateName(dbInheritTemplateName);

		  retEntry = dbData;
	  } else {
		  // some unknown type
		  retEntry = new JNADirEntry();
	  }
	  
	  //read common attributes
	  retEntry.setProperties(dataAsMap);

	  DominoDateTime fileModified = getDateValue(dataAsMap, "$Modified"); //$NON-NLS-1$
	  retEntry.setModified(fileModified);

	  String fileName = null;
	  Object fileNameObj = dataAsMap.get("$TITLE"); //$NON-NLS-1$
	  if (fileNameObj instanceof String) {
		  fileName = (String) fileNameObj;
	  }
	  
	  String filePath = null;
	  Object filePathObj = dataAsMap.get("$path"); //$NON-NLS-1$
	  if (filePathObj instanceof String) {
		  filePath = (String) filePathObj;
	  }

	  String physicalFilePath = null;
	  Object physicalFilePathObj = dataAsMap.get("$PHYSICALPATH"); //$NON-NLS-1$
	  if (physicalFilePathObj instanceof String) {
		  physicalFilePath = (String) physicalFilePathObj;
	  }
	  
	  long fileLength = 0;
	  Object fileLengthObj = dataAsMap.get("$Length"); //$NON-NLS-1$
	  if (fileLengthObj instanceof Number) {
		  fileLength = ((Number)fileLengthObj).longValue();
	  }
	  
	  if (fileName!=null && filePath!=null) {
		  retEntry.setFileName(fileName);
		  retEntry.setFilePath(filePath);
		  retEntry.setPhysicalFilePath(physicalFilePath);
		  retEntry.setFileLength(fileLength);
		  
		  return retEntry;
	  }
	  else {
		  //ignore entries without filename/filepath if they ever exist
		  return null;
	  }
  }

  private DominoDateTime getDateValue(Map data, String key) {
    Object valueObj = data.get(key);
    if (valueObj instanceof DominoDateTime) {
      return (DominoDateTime) valueObj;
    } else if (valueObj instanceof Calendar) {
      return new JNADominoDateTime(((Calendar) valueObj).toInstant());
    }
    return null;
  }

  private static class LoopImpl extends Loop {

    public void next() {
      super.setIndex(getIndex() + 1);
    }

    @Override
    public void setIsLast() {
      super.setIsLast();
    }
  }

  /**
   * Base class for directory scan search results
   * 
   * @author Karsten Lehmann
   */
  public static class JNADirEntry implements DirEntry {
    private Map m_properties;
    private String m_server;
    private String m_fileName;
    private String m_filePath;
    private String m_physicalPath;
    private long m_fileLength;
    private DominoDateTime m_modifiedDate;
    
    @Override
    public Map getProperties() {
      return m_properties;
    }

    /**
     * Sets the raw data of the search result entry
     * 
     * @param rawData data
     */
    void setProperties(Map rawData) {
      this.m_properties = new HashMap<>(rawData);
    }

    @Override
    @SuppressWarnings("unchecked")
    public  T getAdapter(Class clazz) {
      if (clazz.isAssignableFrom(DirEntry.class)) {
        return (T) this;
      }
      return null;
    }

    @Override
    public String getServer() {
      return m_server;
    }

    void setServer(String server) {
      this.m_server = server;
    }

    /**
     * Sets the name of the entry in the data diretory
     * 
     * @param fileName name
     */
    void setFileName(String fileName) {
      this.m_fileName = fileName;
    }

    @Override
    public String getFileName() {
      return this.m_fileName;
    }

    @Override
    public String getFilePath() {
      return m_filePath;
    }

    /**
     * Sets the complete relative path of the file in the
     * data directory
     * 
     * @param filePath path
     */
    void setFilePath(String filePath) {
      this.m_filePath = filePath;
    }

    @Override
    public String getPhysicalFilePath() {
    	return m_physicalPath;
    }

    /**
     * Sets the physical/absolute path of the file in the
     * scanned directory
     * @param filePath path
     */
    void setPhysicalFilePath(String filePath) {
    	this.m_physicalPath = filePath;
    }

    @Override
    public long getFileLength() {
    	return m_fileLength;
    }

    /**
     * Sets the length of the file
     * 
     * @param length length
     */
    void setFileLength(long length) {
    	this.m_fileLength = length;
    }
    
    @Override
    public DominoDateTime getModified() {
    	return m_modifiedDate;
    }

    /**
     * Sets the last modified date
     * 
     * @param modified date
     */
    void setModified(DominoDateTime modified) {
    	this.m_modifiedDate = modified;
    }
    
    @Override
    public String toString() {
      return MessageFormat.format("JNADirEntry [properties={0}, server={1}]", m_properties, m_server); //$NON-NLS-1$
    }

  }

  /**
   * Subclass of {@link DirEntry} that is used to return
   * parsed data of folders.
   * 
   * @author Karsten Lehmann
   */
  public static class JNAFolderData extends JNADirEntry implements FolderData {

    @Override
    @SuppressWarnings("unchecked")
    public  T getAdapter(Class clazz) {
      if (clazz.isAssignableFrom(FolderData.class)) {
        return (T) this;
      }

      return super.getAdapter(clazz);
    }

    @Override
    public String toString() {
      return MessageFormat.format("JNAFolderData [folderpath={0}]", getFilePath()); //$NON-NLS-1$
    }

  }

  /**
   * Subclass of {@link DirEntry} that is used to return
   * parsed data of databases.
   * 
   * @author Karsten Lehmann
   */
  public static class JNADatabaseData extends JNADirEntry implements DatabaseData {
    private String m_title;
    private DominoDateTime m_created;
    private DominoDateTime m_modified;
    private DominoDateTime m_lastFixup;
    private DominoDateTime m_lastCompact;
    private DominoDateTime m_nonDataMod;
    private DominoDateTime m_dataMod;
    private String m_category;
    private String m_templateName;
    private String m_inheritTemplateName;

    /**
     * Returns the database title
     * 
     * @return title
     */
    @Override
    public String getTitle() {
      return m_title;
    }

    /**
     * Sets the database title
     * 
     * @param title title
     */
    private void setTitle(String title) {
      this.m_title = title;
    }

    /**
     * Returns the filename of the database
     * 
     * @return filename
     */
    @Override
    public String getFileName() {
      return super.getFileName();
    }

    /**
     * Sets the filename of the database
     * 
     * @param fileName filename
     */
    @Override
    void setFileName(String fileName) {
      super.setFileName(fileName);
    }

    /**
     * Returns the complete relative path of the database in the data directory
     * 
     * @return path
     */
    @Override
    public String getFilePath() {
      return super.getFilePath();
    }

    /**
     * Sets the complete relative path of the database in the data directory
     * 
     * @param filePath path
     */
    @Override
    void setFilePath(String filePath) {
      super.setFilePath(filePath);
    }

    /**
     * Returns the database creation date
     * 
     * @return creation date
     */
    @Override
    public DominoDateTime getCreated() {
      return m_created;
    }

    /**
     * Sets the database creation date
     * 
     * @param created creation date
     */
    private void setCreated(DominoDateTime created) {
      this.m_created = created;
    }

    /**
     * Returns the date of the last fixup
     * 
     * @return last fixup
     */
    @Override
    public Optional getLastFixup() {
      return Optional.ofNullable(this.m_lastFixup);
    }

    /**
     * Sets the date of the last db fixup
     * 
     * @param lastFixup last fixup
     */
    private void setLastFixup(DominoDateTime lastFixup) {
      this.m_lastFixup = lastFixup;
    }

    /**
     * Returns the date of the last compact
     * 
     * @return last compact
     */
    @Override
    public Optional getLastCompact() {
      return Optional.ofNullable(this.m_lastCompact);
    }

    /**
     * Sets the date of the last db compact
     * 
     * @param lastCompact last compact
     */
    private void setLastCompact(DominoDateTime lastCompact) {
      this.m_lastCompact = lastCompact;
    }

    /**
     * Returns the date of the last design change
     * 
     * @return design modified date
     */
    @Override
    public DominoDateTime getDesignModifiedDate() {
      return this.m_nonDataMod;
    }

    /**
     * Sets the date of the last design change
     * 
     * @param nonDataMod design modified date
     */
    private void setDesignModifiedDate(DominoDateTime nonDataMod) {
      this.m_nonDataMod = nonDataMod;
    }

    /**
     * Returns the date of the last data change
     * 
     * @return data modified date
     */
    public DominoDateTime getDataModifiedDate() {
      return this.m_dataMod;
    }

    /**
     * Sets the date of the last data change
     * 
     * @param dataMod data modified date
     */
    private void setDataModifiedDate(DominoDateTime dataMod) {
      this.m_dataMod = dataMod;
    }


    /**
     * Returns the database category
     * 
     * @return category or empty string
     */
    @Override
    public String getCategory() {
      return this.m_category;
    }

    /**
     * Sets the database category
     * 
     * @param category category
     */
    private void setCategory(String category) {
      this.m_category = category;
    }

    /**
     * Returns the template name
     * 
     * @return template name if this database is a template, empty string otherwise
     */
    @Override
    public String getTemplateName() {
      return m_templateName;
    }

    private void setTemplateName(String templateName) {
      this.m_templateName = templateName;
    }

    /**
     * Returns the name of the template that this database inherits its design from
     * 
     * @return inherit template name or empty string
     */
    @Override
    public String getInheritTemplateName() {
      return m_inheritTemplateName;
    }

    /**
     * Sets the inherit template name
     * 
     * @param inheritTemplateName inherit template name
     */
    private void setInheritTemplateName(String inheritTemplateName) {
      this.m_inheritTemplateName = inheritTemplateName;
    }

    @Override
    @SuppressWarnings("unchecked")
    public  T getAdapter(Class clazz) {
      if (clazz.isAssignableFrom(DatabaseData.class)) {
        return (T) this;
      }

      return super.getAdapter(clazz);
    }

    @Override
    public String toString() {
      return MessageFormat.format(
			"JNADatabaseData [title={0}, filename={1}, filepath={2}, created={3}, modified={4}, lastfixup={5}, lastcompact={6}, datamod={7}, nondatamod={8}, category={9}, templatename={10}, inherittemplateName={11}]", //$NON-NLS-1$
			m_title, getFileName(), getFilePath(), m_created, m_modified, m_lastFixup, m_lastCompact, m_dataMod, m_nonDataMod, m_category, m_templateName,
			m_inheritTemplateName
		);
    }


  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy