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

com.hcl.domino.jna.internal.richtext.JNARichtextNavigator Maven / Gradle / Ivy

There is a newer version: 1.44.0
Show 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.internal.richtext;

import java.lang.ref.ReferenceQueue;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.LinkedList;

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.richtext.RichTextUtil;
import com.hcl.domino.commons.richtext.RichtextNavigator;
import com.hcl.domino.data.Database.Action;
import com.hcl.domino.data.Document;
import com.hcl.domino.exception.ObjectDisposedException;
import com.hcl.domino.jna.BaseJNAAPIObject;
import com.hcl.domino.jna.data.JNADocument;
import com.hcl.domino.jna.data.JNAItem;
import com.hcl.domino.jna.internal.DisposableMemory;
import com.hcl.domino.jna.internal.gc.allocations.JNARichtextNavigatorAllocations;
import com.hcl.domino.richtext.RichTextWriter;
import com.hcl.domino.richtext.records.RichTextRecord;

public class JNARichtextNavigator extends BaseJNAAPIObject implements RichtextNavigator {
	private String m_richTextItemName;
	
	private LinkedList m_items;
	private int m_currentItemIndex = -1;
	
	private LinkedList> m_currentItemRecords;
	private int m_currentItemRecordsIndex = -1;

	public JNARichtextNavigator(JNADocument doc, String richTextItemName) {
		super(doc);
		
		m_richTextItemName = richTextItemName;
		//read items
		m_items = new LinkedList<>();

		doc.forEachItem(richTextItemName, (item, loop) -> {
			if (JNAItem.CD_TYPES.contains(item.getType()) && item instanceof JNAItem) {
				m_items.add((JNAItem) item);
			}
		});

		setInitialized();
	}
	
	/**
	 * Constructs a new rich-text navigator for the specific item within a document.
	 * 
	 * @param doc the parent document
	 * @param item the item containing composite data
	 * @since 1.0.43
	 */
	public JNARichtextNavigator(JNADocument doc, JNAItem item) {
	  super(doc);
    
    m_richTextItemName = item.getName();
    //read items
    m_items = new LinkedList<>();
    m_items.add(item);

    setInitialized();
	}

	@Override
	public Document getParentDocument() {
		return (Document) getParent();
	}
	
	@Override
	public String getItemName() {
		return m_richTextItemName;
	}
	
	@Override
	protected void checkDisposedLocal() {
		Document doc = getParentDocument();
		
		if (doc instanceof JNADocument && ((JNADocument)doc).isDisposed()) {
			throw new ObjectDisposedException(doc);
		}
	}
	
	@SuppressWarnings("rawtypes")
	@Override
	protected JNARichtextNavigatorAllocations createAllocations(IGCDominoClient parentDominoClient,
			APIObjectAllocations parentAllocations, ReferenceQueue queue) {

		return new JNARichtextNavigatorAllocations(parentDominoClient, parentAllocations, this, queue);
	}
	
	@Override
	public RichtextPosition getCurrentPosition() {
		JNARichTextPosition posImpl = new JNARichTextPosition(this, m_currentItemIndex, m_currentItemRecordsIndex);
		return posImpl;
	}

	@Override
	public void restorePosition(RichtextPosition pos) {
		checkDisposed();
		
		if (!(pos instanceof JNARichTextPosition)) {
			throw new IllegalArgumentException("Invalid position, not generated by this navigator");
		}

		JNARichTextPosition posImpl = (JNARichTextPosition) pos;
		if (posImpl.m_parentNav!=this) {
			throw new IllegalArgumentException("Invalid position, not generated by this navigator");
		}
		
		int oldItemIndex = m_currentItemIndex;
		m_currentItemIndex = posImpl.m_itemIndex;
		m_currentItemRecordsIndex = posImpl.m_recordIndex;
		
		if (posImpl.m_itemIndex==-1) {
			m_currentItemRecords = null;
		}
		else if (oldItemIndex!=posImpl.m_itemIndex) {
			//current item changed, so we need to reload the records
			JNAItem currItem = m_items.get(m_currentItemIndex);
			m_currentItemRecords = readCDRecords(currItem);
		}
	}
	
	@Override
	public boolean isEmpty() {
		checkDisposed();
		
		if (m_items.isEmpty()) {
			return true;
		}
		else {
			boolean hasRecords = hasCDRecords(m_items.getFirst());
			return !hasRecords;
		}
	}
	
	@Override
	public boolean gotoFirst() {
		checkDisposed();
		
		if (isEmpty()) {
			m_currentItemIndex = -1;
			m_currentItemRecords = null;
			m_currentItemRecordsIndex = -1;
			return false;
		}
		else if (m_currentItemRecords==null || m_currentItemIndex!=0) {
			//move to first item
			JNAItem firstItem = m_items.getFirst();
			m_currentItemRecords = readCDRecords(firstItem);
			m_currentItemIndex = 0;
		}
		
		//move to first record
		if (m_currentItemRecords.isEmpty()) {
			m_currentItemRecordsIndex = -1;
			return false;
		}
		else {
			m_currentItemRecordsIndex = 0;
			return true;
		}
	}
	
	private boolean hasCDRecords(JNAItem item) {
		final boolean[] hasRecords = new boolean[1];
		
		item.enumerateCDRecords((signature, cdRecordPtr, cdRecordLength) -> {
			hasRecords[0] = true;
			return Action.Stop;
		});
	
		return hasRecords[0];
	}
	
	/**
	 * Copies all CD records from the specified item
	 * 
	 * @param item item
	 * @return list with CD record data
	 */
	private LinkedList> readCDRecords(JNAItem item) {
		final LinkedList> itemRecords = new LinkedList<>();

		item.enumerateCDRecords((signature, cdRecordPtr, cdRecordLength) -> {
			byte[] cdRecordDataArr = cdRecordPtr.getByteArray(0, cdRecordLength);
			@SuppressWarnings("resource")
      DisposableMemory cdRecordDataMem = new DisposableMemory(cdRecordLength);
			cdRecordDataMem.write(0, cdRecordDataArr, 0, cdRecordLength);
			ByteBuffer data = cdRecordDataMem.getByteBuffer(0, cdRecordLength).order(ByteOrder.nativeOrder());

			RichTextRecord record = RichTextUtil.encapsulateRecord(signature, data);
			itemRecords.add(record);
			return Action.Continue;
		});
	
		return itemRecords;
	}
	
	@Override
	public boolean gotoLast() {
		checkDisposed();
		
		if (isEmpty()) {
			m_currentItemIndex = -1;
			m_currentItemRecords = null;
			m_currentItemRecordsIndex = -1;
			return false;
		}
		else if (m_currentItemIndex!=(m_items.size()-1)) {
			//move to last item
			JNAItem lastItem = m_items.getLast();
			m_currentItemRecords = readCDRecords(lastItem);
			m_currentItemIndex = m_items.size()-1;
		}
		
		//move to last record
		if (m_currentItemRecords.isEmpty()) {
			m_currentItemRecordsIndex = -1;
			return false;
		}
		else {
			m_currentItemRecordsIndex = m_currentItemRecords.size()-1;
			return true;
		}
	}
	
	@Override
	public boolean gotoNext() {
		checkDisposed();
		
		if (isEmpty()) {
			m_currentItemIndex = -1;
			m_currentItemRecords = null;
			m_currentItemRecordsIndex = -1;
			return false;
		}
		else {
			if (m_currentItemRecordsIndex==-1) {
				//offroad?
				return false;
			}
			else if (m_currentItemRecordsIndex<(m_currentItemRecords.size()-1)) {
				//more records available for current item
				m_currentItemRecordsIndex++;
				return true;
			}
			else {
				//move to next item
				
				if (m_currentItemIndex==-1) {
					//offroad?
					return false;
				}
				else if (m_currentItemIndex<(m_items.size()-1)) {
					//move items available
					m_currentItemIndex++;
					JNAItem currItem = m_items.get(m_currentItemIndex);
					m_currentItemRecords = readCDRecords(currItem);
					if (m_currentItemRecords.isEmpty()) {
						m_currentItemRecordsIndex = -1;
						return false;
					}
					else {
						//more to first record of that item
						m_currentItemRecordsIndex = 0;
						return true;
					}
				}
				else {
					//no more items available
					return false;
				}
			}
		}
	}
	
	@Override
	public boolean gotoPrev() {
		checkDisposed();
		
		if (isEmpty()) {
			m_currentItemIndex = -1;
			m_currentItemRecords = null;
			m_currentItemRecordsIndex = -1;
			return false;
		}
		else {
			if (m_currentItemRecordsIndex==-1) {
				//offroad?
				return false;
			}
			else if (m_currentItemRecordsIndex>0) {
				//more records available for current item
				m_currentItemRecordsIndex--;
				return true;
			}
			else {
				//move to prev item
				
				if (m_currentItemIndex==-1) {
					//offroad?
					return false;
				}
				else if (m_currentItemIndex>0) {
					//move items available
					m_currentItemIndex--;
					JNAItem currItem = m_items.get(m_currentItemIndex);
					m_currentItemRecords = readCDRecords(currItem);
					if (m_currentItemRecords.isEmpty()) {
						m_currentItemRecordsIndex = -1;
						return false;
					}
					else {
						//more to last record of that item
						m_currentItemRecordsIndex = m_currentItemRecords.size()-1;
						return true;
					}
				}
				else {
					//no more items available
					return false;
				}
			}
		}
	}
	
	@Override
	public boolean hasNext() {
		checkDisposed();
		
		if (m_items.isEmpty()) {
			return false;
		}
		else {
			if (m_currentItemRecordsIndex==-1) {
				//offroad?
				return false;
			}
			else if (m_currentItemRecordsIndex<(m_currentItemRecords.size()-1)) {
				//more records available for current item
				return true;
			}
			else {
				if (m_currentItemIndex==-1) {
					//offroad?
					return false;
				}
				else if (m_currentItemIndex<(m_items.size()-1)) {
					//more items available
					JNAItem currItem = m_items.get(m_currentItemIndex+1);
					boolean hasRecords = hasCDRecords(currItem);
					return hasRecords;
				}
				else {
					//no more items available
					return false;
				}
			}
		}
	}
	
	@Override
	public boolean hasPrev() {
		checkDisposed();
		
		if (m_items.isEmpty()) {
			return false;
		}
		else {
			if (m_currentItemRecordsIndex==-1) {
				//offroad?
				return false;
			}
			else if (m_currentItemRecordsIndex>0) {
				//more records available for current item
				return true;
			}
			else {
				//move to prev item
				
				if (m_currentItemIndex==-1) {
					//offroad?
					return false;
				}
				else if (m_currentItemIndex>0) {
					//move items available
					JNAItem currItem = m_items.get(m_currentItemIndex-1);
					boolean hasRecords = hasCDRecords(currItem);
					return hasRecords;
				}
				else {
					//no more items available
					return false;
				}
			}
		}
	}
	
	@Override
	public RichTextRecord getCurrentRecord() {
		if (m_currentItemRecordsIndex==-1) {
			return null;
		}
		else {
			RichTextRecord record = m_currentItemRecords.get(m_currentItemRecordsIndex);
			return record;
		}
	}
	
	@Override
	public ByteBuffer getCurrentRecordData() {
		RichTextRecord record = getCurrentRecord();
		if (record==null) {
			return null;
		}
		else {
			return record.getDataWithoutHeader();
		}
	}

	@Override
	public ByteBuffer getCurrentRecordDataWithHeader() {
		RichTextRecord record = getCurrentRecord();
		if (record==null) {
			return null;
		}
		else {
			return record.getData();
		}
	}
	
	@Override
	public int getCurrentRecordHeaderLength() {
		RichTextRecord record = getCurrentRecord();
		if (record==null) {
			return 0;
		}
		else {
			return record.getRecordHeaderLength();
		}
	}
	
	@Override
	public short getCurrentRecordTypeConstant() {
		RichTextRecord record = getCurrentRecord();
		return record==null ? 0 : record.getTypeValue();
	}
	
	@Override
	public int getCurrentRecordDataLength() {
		RichTextRecord record = getCurrentRecord();
		return record==null ? 0 : record.getPayloadLength();
	}
	
	@Override
	public int getCurrentRecordTotalLength() {
		RichTextRecord record = getCurrentRecord();
		return record==null ? 0 : record.getCDRecordLength();
	}
	
	@Override
	public void copyCurrentRecordTo(RichTextWriter ct) {
		RichTextRecord record = getCurrentRecord();
		if (record==null) {
			throw new IllegalStateException("Current record is null");
		}
		
		ct.addRichTextRecord(record);
	}
	
	public static class JNARichTextPosition implements RichtextPosition {
		private RichtextNavigator m_parentNav;
		private int m_itemIndex;
		private int m_recordIndex;
		
		public JNARichTextPosition(RichtextNavigator parentNav, int itemIndex, int recordIndex) {
			m_parentNav = parentNav;
			m_itemIndex = itemIndex;
			m_recordIndex = recordIndex;
		}

		@Override
		public int hashCode() {
			RichtextNavigator nav = m_parentNav;
			
			final int prime = 31;
			int result = 1;
			result = prime * result + (nav == null ? 0 : nav.hashCode());
			result = prime * result + m_itemIndex;
			result = prime * result + (nav == null ? 0 : nav.hashCode());
			result = prime * result + m_recordIndex;
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj) {
				return true;
			}
			if (obj == null) {
				return false;
			}
			if (getClass() != obj.getClass()) {
				return false;
			}
			JNARichTextPosition other = (JNARichTextPosition) obj;
			RichtextNavigator nav = m_parentNav;
			if(nav == null && other.m_parentNav != null) {
				return false;
			}
			if (nav != null && !nav.equals(other.m_parentNav)) {
				return false;
			}
			if (m_itemIndex != other.m_itemIndex) {
				return false;
			}
			if (m_recordIndex != other.m_recordIndex) {
				return false;
			}
			return true;
		}
		
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy