/* (non-Javadoc)
  * @see org.alfresco.service.cmr.view.Exporter#content(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, java.io.InputStream)
  */
 public void content(
     NodeRef nodeRef, QName property, InputStream content, ContentData contentData, int index) {
   try {
     byte[] data = new byte[1048576]; // 1MB
     int read = content.read(data);
     ArrayList<Byte> total = new ArrayList<Byte>();
     while (read > -1) {
       for (int i = 0; i < read; i++) {
         total.add(data[i]);
       }
       read = content.read(data);
     }
     byte[] toEncode = new byte[total.size()];
     for (int i = 0; i < toEncode.length; i++) {
       toEncode[i] = total.get(i).byteValue();
     }
     String temp = Base64.encodeBytes(toEncode);
     String ptemp =
         contentData.getMimetype()
             + "|"
             + contentData.getSize()
             + "|"
             + contentData.getEncoding()
             + "|";
     contentHandler.characters(ptemp.toCharArray(), 0, ptemp.length());
     contentHandler.characters(temp.toCharArray(), 0, temp.length());
   } catch (Exception e) {
     throw new ExporterException("Failed to process export of base64 content: " + e.getMessage());
   }
 }
  public void testImmediateRemoval() throws Exception {
    eagerCleaner.setEagerOrphanCleanup(false);

    final StoreRef storeRef = nodeService.createStore("test", getName() + "-" + GUID.generate());
    RetryingTransactionCallback<ContentData> testCallback =
        new RetryingTransactionCallback<ContentData>() {
          public ContentData execute() throws Throwable {
            // Create some content
            NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
            Map<QName, Serializable> properties = new HashMap<QName, Serializable>(13);
            properties.put(ContentModel.PROP_NAME, (Serializable) "test.txt");
            NodeRef contentNodeRef =
                nodeService
                    .createNode(
                        rootNodeRef,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.TYPE_CONTENT,
                        properties)
                    .getChildRef();
            ContentWriter writer =
                contentService.getWriter(contentNodeRef, ContentModel.PROP_CONTENT, true);
            writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
            writer.putContent("INITIAL CONTENT");
            ContentData contentData = writer.getContentData();

            // Delete the first node
            nodeService.deleteNode(contentNodeRef);

            // Done
            return contentData;
          }
        };
    ContentData contentData =
        transactionService.getRetryingTransactionHelper().doInTransaction(testCallback);
    // Make sure that the content URL still exists
    ContentReader reader = contentService.getRawReader(contentData.getContentUrl());
    assertNotNull(reader);
    assertTrue("Content should not have been eagerly deleted.", reader.exists());

    // fire the cleaner
    cleaner.setProtectDays(0);
    cleaner.execute();

    reader = contentService.getRawReader(contentData.getContentUrl());
    // the content should have disappeared as it is not in the database
    assertFalse("Unprotected content was not deleted", reader.exists());
    assertTrue("Content listener was not called", deletedUrls.contains(reader.getContentUrl()));
  }
  /**
   * Relink the content data from a new node to an existing node to preserve the version history.
   *
   * @param tempNodeRef temp nodeRef
   * @param nodeToMoveRef NodeRef
   * @param newParentNodeRef NodeRef
   * @param newName new name
   */
  public void relinkNode(
      NodeRef tempNodeRef, NodeRef nodeToMoveRef, NodeRef newParentNodeRef, String newName)
      throws FileNotFoundException, FileExistsException {
    // Get the properties for the old and new nodes
    org.alfresco.service.cmr.model.FileInfo tempFileInfo =
        fileFolderService.getFileInfo(tempNodeRef);
    org.alfresco.service.cmr.model.FileInfo fileToMoveInfo =
        fileFolderService.getFileInfo(nodeToMoveRef);

    // Save the current name of the old node
    String tempName = tempFileInfo.getName();

    try {
      // Rename operation will add or remove the sys:temporary aspect appropriately

      // rename temp file to the new name
      fileFolderService.rename(tempNodeRef, newName);

      // rename new file to old name
      fileFolderService.rename(nodeToMoveRef, tempName);
    } catch (org.alfresco.service.cmr.model.FileNotFoundException e) {
      throw new FileNotFoundException(e.getMessage());
    } catch (org.alfresco.service.cmr.model.FileExistsException e) {
      throw new FileExistsException(e.getMessage());
    }

    if (!tempFileInfo.isFolder() && !fileToMoveInfo.isFolder()) {
      // swap the content between the two
      ContentData oldContentData = tempFileInfo.getContentData();
      if (oldContentData == null) {
        String mimetype = mimetypeService.guessMimetype(tempName);
        oldContentData = ContentData.setMimetype(null, mimetype);
      }

      ContentData newContentData = fileToMoveInfo.getContentData();

      // Reset the mime type
      // TODO Pass the content along when guessing the mime type, so we're more accurate
      String mimetype = mimetypeService.guessMimetype(newName);
      newContentData = ContentData.setMimetype(newContentData, mimetype);

      nodeService.setProperty(tempNodeRef, ContentModel.PROP_CONTENT, newContentData);
      nodeService.setProperty(nodeToMoveRef, ContentModel.PROP_CONTENT, oldContentData);
    }
  }
  /**
   * Helper method to extract file info from a specific node.
   *
   * <p>This method goes direct to the repo for all information and no data is cached here.
   *
   * @param nodeRef the node
   * @param readOnly, should the file be shown as "read only", regardless of its permissions?
   * @param lockedFilesAsOffline should a locked file be marked as offline
   * @return Returns the file information pertinent to the node
   * @throws FileNotFoundException if the path refers to a non-existent file
   */
  private ContentFileInfo getFileInformationImpl(
      NodeRef nodeRef, boolean readOnly, boolean lockedFilesAsOffline)
      throws FileNotFoundException {
    // get the file info
    org.alfresco.service.cmr.model.FileInfo fileFolderInfo = fileFolderService.getFileInfo(nodeRef);

    // retrieve required properties and create new JLAN file info
    ContentFileInfo fileInfo = new ContentFileInfo(nodeRef);

    // Set the file id from the node's DBID
    long id =
        DefaultTypeConverter.INSTANCE.convert(
            Long.class, nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_DBID));
    fileInfo.setFileId((int) (id & 0xFFFFFFFFL));

    // unset all attribute flags
    int fileAttributes = 0;
    fileInfo.setFileAttributes(fileAttributes);

    if (fileFolderInfo.isFolder()) {
      // add directory attribute
      fileAttributes |= FileAttribute.Directory;
      fileInfo.setFileAttributes(fileAttributes);
      fileInfo.setFileType(FileType.Directory);
    } else {
      Map<QName, Serializable> nodeProperties = fileFolderInfo.getProperties();

      // Get the file size from the content

      ContentData contentData = (ContentData) nodeProperties.get(ContentModel.PROP_CONTENT);
      long size = 0L;
      if (contentData != null) {
        size = contentData.getSize();
      }
      fileInfo.setSize(size);

      // Set the allocation size by rounding up the size to a 512 byte block boundary

      if (size > 0) {
        fileInfo.setAllocationSize((size + 512L) & 0xFFFFFFFFFFFFFE00L);
      }

      // Check whether the file is locked

      if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) {
        LockType lockType = lockService.getLockType(nodeRef);

        int attr = fileInfo.getFileAttributes();

        if (lockType != null) {
          switch (lockType) {
            case NODE_LOCK:
              if ((attr & FileAttribute.ReadOnly) == 0) attr += FileAttribute.ReadOnly;
              break;
            case WRITE_LOCK:
              LockStatus lockStatus = lockService.getLockStatus(nodeRef);
              if (lockStatus == LockStatus.LOCK_OWNER) {
              } else {
                if ((attr & FileAttribute.ReadOnly) == 0) {
                  attr += FileAttribute.ReadOnly;
                }

                if (lockedFilesAsOffline) {
                  attr += FileAttribute.NTOffline;
                }
              }
              break;
            case READ_ONLY_LOCK:
              if ((attr & FileAttribute.ReadOnly) == 0) {
                attr += FileAttribute.ReadOnly;
              }

              if (lockedFilesAsOffline) {
                attr += FileAttribute.NTOffline;
              }
              break;
          }

          fileInfo.setFileAttributes(attr);
        }
      }

      // Check if it is a link node

      if (fileFolderInfo.isLink()) {
        fileInfo.setLinkNodeRef(fileFolderInfo.getLinkNodeRef());
      }
    }

    // created
    Date createdDate = fileFolderInfo.getCreatedDate();
    if (createdDate != null) {
      long created = DefaultTypeConverter.INSTANCE.longValue(createdDate);
      fileInfo.setCreationDateTime(created);
    }
    // modified
    Date modifiedDate = fileFolderInfo.getModifiedDate();
    if (modifiedDate != null) {
      long modified = DefaultTypeConverter.INSTANCE.longValue(modifiedDate);
      fileInfo.setModifyDateTime(modified);
      fileInfo.setAccessDateTime(modified);
      fileInfo.setChangeDateTime(modified);
    }
    // name
    String name = fileFolderInfo.getName();
    if (name != null) {
      fileInfo.setFileName(name);

      // Check for file names that should be hidden
      if (hiddenAspect.getVisibility(Client.cifs, fileInfo.getNodeRef())
          == Visibility.HiddenAttribute) {
        // Add the hidden file attribute
        int attr = fileInfo.getFileAttributes();
        if ((attr & FileAttribute.Hidden) == 0) {
          attr += FileAttribute.Hidden;
          fileInfo.setFileAttributes(attr);
        }
      }
    }

    // Read/write access

    if (!fileFolderInfo.isFolder() || isReadOnlyFlagOnFolders) {
      boolean deniedPermission =
          permissionService.hasPermission(nodeRef, PermissionService.WRITE) == AccessStatus.DENIED;
      if (readOnly || deniedPermission) {
        int attr = fileInfo.getFileAttributes();
        if ((attr & FileAttribute.ReadOnly) == 0) {
          attr += FileAttribute.ReadOnly;
          fileInfo.setFileAttributes(attr);
        }
      }
    }

    // Set the normal file attribute if no other attributes are set

    if (fileInfo.getFileAttributes() == 0) fileInfo.setFileAttributes(FileAttribute.NTNormal);

    // Debug

    if (logger.isDebugEnabled()) {
      logger.debug("Fetched file info: \n" + "   info: " + fileInfo);
    }

    // Return the file information

    return fileInfo;
  }
  /**
   * @see
   *     org.alfresco.repo.version.operations.VersionOperationsService#checkin(org.alfresco.repo.ref.NodeRef,
   *     Map<String,Serializable>, java.lang.String, boolean)
   */
  public NodeRef checkin(
      NodeRef workingCopyNodeRef,
      Map<String, Serializable> versionProperties,
      String contentUrl,
      boolean keepCheckedOut) {
    NodeRef nodeRef = null;

    // Check that we have been handed a working copy
    if (this.nodeService.hasAspect(workingCopyNodeRef, ContentModel.ASPECT_WORKING_COPY) == false) {
      // Error since we have not been passed a working copy
      throw new AspectMissingException(ContentModel.ASPECT_WORKING_COPY, workingCopyNodeRef);
    }

    // Check that the working node still has the copy aspect applied
    if (this.nodeService.hasAspect(workingCopyNodeRef, ContentModel.ASPECT_COPIEDFROM) == true) {
      // Invoke policy
      invokeBeforeCheckIn(workingCopyNodeRef, versionProperties, contentUrl, keepCheckedOut);

      Map<QName, Serializable> workingCopyProperties =
          nodeService.getProperties(workingCopyNodeRef);
      // Try and get the original node reference
      nodeRef = (NodeRef) workingCopyProperties.get(ContentModel.PROP_COPY_REFERENCE);
      if (nodeRef == null) {
        // Error since the original node can not be found
        throw new CheckOutCheckInServiceException(MSG_ERR_BAD_COPY);
      }

      try {
        // Release the lock
        this.lockService.unlock(nodeRef);
      } catch (UnableToReleaseLockException exception) {
        throw new CheckOutCheckInServiceException(MSG_ERR_NOT_OWNER, exception);
      }

      if (contentUrl != null) {
        ContentData contentData =
            (ContentData) workingCopyProperties.get(ContentModel.PROP_CONTENT);
        if (contentData == null) {
          throw new AlfrescoRuntimeException(
              MSG_ERR_WORKINGCOPY_HAS_NO_MIMETYPE, new Object[] {workingCopyNodeRef});
        } else {
          contentData =
              new ContentData(
                  contentUrl,
                  contentData.getMimetype(),
                  contentData.getSize(),
                  contentData.getEncoding());
        }
        // Set the content url value onto the working copy
        this.nodeService.setProperty(workingCopyNodeRef, ContentModel.PROP_CONTENT, contentData);
      }

      // Copy the contents of the working copy onto the original
      this.copyService.copy(workingCopyNodeRef, nodeRef);

      // Handle name change on working copy (only for folders/files)
      if (fileFolderService.getFileInfo(workingCopyNodeRef) != null) {
        String origName = (String) this.nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
        String name =
            (String) this.nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_NAME);
        if (hasWorkingCopyNameChanged(name, origName)) {
          // ensure working copy has working copy label in its name to avoid name clash
          if (!name.contains(" " + getWorkingCopyLabel())) {
            try {
              fileFolderService.rename(workingCopyNodeRef, createWorkingCopyName(name));
            } catch (FileExistsException e) {
              throw new CheckOutCheckInServiceException(
                  e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name));
            } catch (FileNotFoundException e) {
              throw new CheckOutCheckInServiceException(
                  e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name));
            }
          }
          try {
            // rename original to changed working name
            fileFolderService.rename(nodeRef, getNameFromWorkingCopyName(name));
          } catch (FileExistsException e) {
            throw new CheckOutCheckInServiceException(
                e, MSG_ERR_CANNOT_RENAME, origName, getNameFromWorkingCopyName(name));
          } catch (FileNotFoundException e) {
            throw new CheckOutCheckInServiceException(
                e, MSG_ERR_CANNOT_RENAME, name, getNameFromWorkingCopyName(name));
          }
        }
      }

      if (versionProperties != null
          && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) {
        // Create the new version
        this.versionService.createVersion(nodeRef, versionProperties);
      }

      if (keepCheckedOut == false) {
        // Delete the working copy
        this.nodeService.deleteNode(workingCopyNodeRef);
      } else {
        // Re-lock the original node
        this.lockService.lock(nodeRef, LockType.READ_ONLY_LOCK);
      }

      // Invoke policy
      invokeOnCheckIn(nodeRef);
    } else {
      // Error since the copy aspect is missing
      throw new AspectMissingException(ContentModel.ASPECT_COPIEDFROM, workingCopyNodeRef);
    }

    return nodeRef;
  }
  /**
   * Create ContentData set it on a Node, delete the Node, then set the ContentData on a new node
   * and check that the content is preserved during eager cleanup.
   */
  public void testEagerCleanupDereferencing() throws Exception {
    eagerCleaner.setEagerOrphanCleanup(true);

    final StoreRef storeRef = nodeService.createStore("test", getName() + "-" + GUID.generate());
    RetryingTransactionCallback<ContentData> testCallback =
        new RetryingTransactionCallback<ContentData>() {
          public ContentData execute() throws Throwable {
            // Create some content
            NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
            Map<QName, Serializable> properties = new HashMap<QName, Serializable>(13);
            properties.put(ContentModel.PROP_NAME, (Serializable) "test.txt");
            NodeRef contentNodeRef =
                nodeService
                    .createNode(
                        rootNodeRef,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.TYPE_CONTENT,
                        properties)
                    .getChildRef();
            ContentWriter writer =
                contentService.getWriter(contentNodeRef, ContentModel.PROP_CONTENT, true);
            writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
            writer.putContent("INITIAL CONTENT");
            ContentData contentData = writer.getContentData();

            // Delete the first node
            nodeService.deleteNode(contentNodeRef);

            ContentReader reader = contentService.getRawReader(contentData.getContentUrl());
            assertNotNull(reader);
            assertTrue("Content was cleaned before end of transaction", reader.exists());

            // Make a new copy using the same ContentData
            properties.put(ContentModel.PROP_NAME, (Serializable) "test2.txt");
            properties.put(ContentModel.PROP_CONTENT, contentData);
            contentNodeRef =
                nodeService
                    .createNode(
                        rootNodeRef,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.ASSOC_CHILDREN,
                        ContentModel.TYPE_CONTENT,
                        properties)
                    .getChildRef();

            reader = contentService.getRawReader(contentData.getContentUrl());
            assertNotNull(reader);
            assertTrue("Content was cleaned before end of transaction", reader.exists());

            // Done
            return contentData;
          }
        };
    ContentData contentData =
        transactionService.getRetryingTransactionHelper().doInTransaction(testCallback);
    // Make sure that the content URL still exists
    ContentReader reader = contentService.getRawReader(contentData.getContentUrl());
    assertNotNull(reader);
    assertTrue(
        "Content was cleaned despite being re-referenced in the transaction", reader.exists());
  }