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

com.hcl.domino.jna.design.JNANativeDesignSupport Maven / Gradle / Ivy

There is a newer version: 1.41.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.design;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;

import com.hcl.domino.commons.gc.APIObjectAllocations;
import com.hcl.domino.commons.structures.MemoryStructureUtil;
import com.hcl.domino.commons.util.NotesErrorUtils;
import com.hcl.domino.commons.util.StringUtil;
import com.hcl.domino.data.Document;
import com.hcl.domino.data.DocumentClass;
import com.hcl.domino.data.Item;
import com.hcl.domino.data.Item.ItemFlag;
import com.hcl.domino.data.ItemDataType;
import com.hcl.domino.design.NativeDesignSupport;
import com.hcl.domino.data.NativeItemCoder;
import com.hcl.domino.data.NativeItemCoder.LmbcsVariant;
import com.hcl.domino.jna.data.JNADatabase;
import com.hcl.domino.jna.data.JNADocument;
import com.hcl.domino.jna.data.JNAItem;
import com.hcl.domino.jna.internal.Mem;
import com.hcl.domino.jna.internal.NotesStringUtils;
import com.hcl.domino.jna.internal.capi.NotesCAPI;
import com.hcl.domino.jna.internal.gc.allocations.JNADatabaseAllocations;
import com.hcl.domino.jna.internal.gc.allocations.JNADocumentAllocations;
import com.hcl.domino.jna.internal.gc.handles.DHANDLE;
import com.hcl.domino.jna.internal.gc.handles.LockUtil;
import com.hcl.domino.misc.NotesConstants;
import com.hcl.domino.misc.Pair;
import com.hcl.domino.richtext.structures.ObjectDescriptor;
import com.hcl.domino.richtext.structures.ObjectDescriptor.ObjectType;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

/**
 * 
 * @author Karsten Lehmann
 * @since 1.0.43
 */
public class JNANativeDesignSupport implements NativeDesignSupport {

  @Override
  public Pair formatLSForDesigner(String code, String nameOfContextClass) {
    Memory codeMem = NotesStringUtils.toLMBCS(code, true, false); //add null terminator, keep newlines
    byte[] codeLMBCS = new byte[(int) codeMem.size()];
    codeMem.read(0, codeLMBCS, 0, codeLMBCS.length);
    DHANDLE.ByReference hSrcByRef = DHANDLE.newInstanceByReference();
    short result = Mem.OSMemAlloc((short) 0, codeLMBCS.length, hSrcByRef);
    NotesErrorUtils.checkResult(result);

    byte[] nameOfContextClassLMBCS;
    DHANDLE.ByReference phDataByRef = DHANDLE.newInstanceByReference();
    if (!StringUtil.isEmpty(nameOfContextClass)) {
      nameOfContextClassLMBCS = nameOfContextClass.getBytes(NativeItemCoder.get().getLmbcsCharset(LmbcsVariant.NULLTERM));
      if (nameOfContextClassLMBCS.length > (NotesConstants.MAXIMUM_ID_NAME_LENGTH+1)) {
        throw new IllegalArgumentException(MessageFormat.format("Name of context class exceeds max length of {0} bytes", NotesConstants.MAXIMUM_ID_NAME_LENGTH));
      }
      
      // allocate space for SCRIPTCONTEXTDESCR structure that describes the context of the LS code,
      // e.g. it adds binding code for UI document/view/button
      
      // typedef struct {
      //   DWORD Length;
      //   char  szNameOfContextClass[MAXIMUM_ID_NAME_LENGTH + 1];
      // }SCRIPTCONTEXTDESCR;

      result = Mem.OSMemAlloc((short) 0, 4 + NotesConstants.MAXIMUM_ID_NAME_LENGTH +1, phDataByRef);
      NotesErrorUtils.checkResult(result);
    }
    else {
      nameOfContextClassLMBCS = null;
    }
    
    return LockUtil.lockHandles(hSrcByRef, phDataByRef, (hSrcByVal, phDataByVal) -> {
      try {
        //write code to memory block
        Mem.OSLockObject(hSrcByVal, (ptr) -> {
          ptr.write(0, codeLMBCS, 0, codeLMBCS.length);
          
          return null;
        });
        
        if (!StringUtil.isEmpty(nameOfContextClass) && !phDataByVal.isNull()) {
          //write name of context class to memory block if set
          Mem.OSLockObject(phDataByVal, (ptr) -> {
            ptr.setInt(0, 4 + NotesConstants.MAXIMUM_ID_NAME_LENGTH +1); //sizeof(SCRIPTCONTEXTDESCR)
            ptr.write(4, nameOfContextClassLMBCS, 0, nameOfContextClassLMBCS.length);
            
            return null;
          });
        }
        
        //now format the LS:
        DHANDLE.ByReference hDest = DHANDLE.newInstanceByReference();
        DHANDLE.ByReference hErrs = DHANDLE.newInstanceByReference();
        int dwFlags = 0;
        
        short resultFormat = NotesCAPI.get().AgentLSTextFormat(hSrcByVal, hDest,
            hErrs, dwFlags, phDataByRef);
        //AgentLSTextFormat only returns NOERROR or ERR_MEMORY in case it runs out of memory
        NotesErrorUtils.checkResult(resultFormat);
        
        String formattedCode = lockAndReadText(hDest, true);
        String errorTxt = lockAndReadText(hErrs, true);
        
        return new Pair<>(formattedCode, errorTxt);
      }
      finally {
        Mem.OSMemFree(hSrcByVal);
        
        if (phDataByVal!=null && !phDataByVal.isNull()) {
          Mem.OSMemFree(phDataByVal);
        }
      }
    });
  }

  private String lockAndReadText(DHANDLE.ByReference hdl, boolean free) {
    if (hdl.isNull()) {
      return ""; //$NON-NLS-1$
    }
    
    return LockUtil.lockHandle(hdl, (hdlByVal) -> {
      Pointer ptrTxt = Mem.OSLockObject(hdlByVal);
      try {
        return NotesStringUtils.fromLMBCS(ptrTxt, -1);
      }
      finally {
        Mem.OSUnlockObject(hdlByVal);
        if (free) {
          Mem.OSMemFree(hdlByVal);
        }
      }
    });
  }
  
  @Override
  public List splitAsLMBCS(String txt, boolean addNull, boolean replaceLinebreaks, int chunkSize) {
    return NotesStringUtils.splitAsLMBCS(txt, addNull, replaceLinebreaks, chunkSize);
  }

  @Override
  public void setCDRecordItemType(Document doc, Item item, ItemDataType newType) {
    if (!isInCDRecordFormat(item.getType())) {
      throw new IllegalArgumentException(MessageFormat.format("Item is not in CD record format: {0}", item.getType()));
    }
    if (!isInCDRecordFormat(newType)) {
      throw new IllegalArgumentException(MessageFormat.format("New item type is not in CD record format: {0}", newType));
    }
    
    ((JNAItem)item).setItemType(newType);
  }
  
  private boolean isInCDRecordFormat(ItemDataType type) {
    return type==ItemDataType.TYPE_COMPOSITE ||
        type==ItemDataType.TYPE_QUERY ||
        type==ItemDataType.TYPE_ACTION;
  }

  private final byte[] initialRunInfoData = new byte[] {
      0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
      0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x10, 0x20, 0x43, 0x00,
      0x61, (byte) 0x87, 0x25, (byte) 0xc1, (byte) 0x8b, 0x44, 0x34, 0x00,
      0x2d, (byte) 0x87, 0x25, (byte) 0xc1, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  };

  @Override
  public void initAgentRunInfo(Document doc) {
    JNADatabase db = (JNADatabase) doc.getParentDatabase();
    JNADatabaseAllocations dbAllocations = (JNADatabaseAllocations) db.getAdapter(APIObjectAllocations.class);
    dbAllocations.checkDisposed();
    
    JNADocumentAllocations docAllocations = (JNADocumentAllocations) ((JNADocument)doc).getAdapter(APIObjectAllocations.class);
    docAllocations.checkDisposed();

    //TODO replace this hardcoded run info data with code that writes AssistRunObjectHeader, AssistRunObjectEntry and AssistRunInfo
    byte[] runInfoDataWithSpace = new byte[1032];
    Arrays.fill(runInfoDataWithSpace, (byte) 0xaa);
    System.arraycopy(initialRunInfoData, 0, runInfoDataWithSpace, 0, initialRunInfoData.length);
    
    short noteClass = DocumentClass.DOCUMENT.getValue();
    short privs = 0;
    ObjectType objectType = ObjectType.ASSIST_RUNDATA;
    
    final DHANDLE.ByReference retCopyBufferHandle = DHANDLE.newInstanceByReference();
    short result = Mem.OSMemAlloc((short) 0, runInfoDataWithSpace.length, retCopyBufferHandle);
    NotesErrorUtils.checkResult(result);

    int rrv = LockUtil.lockHandles(dbAllocations.getDBHandle(), docAllocations.getNoteHandle(), retCopyBufferHandle,
        (hDbByVal, hNoteByVal, hCopyBufferByVal) -> {

          IntByReference rtnRRV = new IntByReference();

          short allocObjResult = NotesCAPI.get().NSFDbAllocObjectExtended2(hDbByVal,
              runInfoDataWithSpace.length,
              noteClass, privs, objectType.getValue(), rtnRRV);
          NotesErrorUtils.checkResult(allocObjResult);

          //copy buffer array data into memory buffer
          Pointer ptrBuffer = Mem.OSLockObject(hCopyBufferByVal);
          try {
            ptrBuffer.write(0, runInfoDataWithSpace, 0, runInfoDataWithSpace.length);
          }
          finally {
            Mem.OSUnlockObject(hCopyBufferByVal);
          }

          short writeObjResult = NotesCAPI.get().NSFDbWriteObject(
              hDbByVal,
              rtnRRV.getValue(),
              hCopyBufferByVal,
              0,
              runInfoDataWithSpace.length);
          NotesErrorUtils.checkResult(writeObjResult);

          Mem.OSMemFree(hCopyBufferByVal);

          return rtnRRV.getValue();
        });

    ObjectDescriptor objDescriptor = MemoryStructureUtil.newStructure(ObjectDescriptor.class, 0);
    objDescriptor.setRRV(rrv);
    objDescriptor.setObjectType(objectType);
    
    ByteBuffer objDescriptorData = objDescriptor.getData();
    ByteBuffer objDescriptorDataWithType = ByteBuffer.allocate(2 + objDescriptorData.limit());
    objDescriptorDataWithType.putShort(ItemDataType.TYPE_OBJECT.getValue());
    objDescriptorDataWithType.put(objDescriptorData);
    objDescriptorDataWithType.position(0);
    
    doc.replaceItemValue(NotesConstants.ASSIST_RUNINFO_ITEM, EnumSet.of(ItemFlag.SUMMARY), objDescriptorDataWithType);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy