/**
   * 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);
    }
  }
 @Override
 public String guessEncoding(InputStream in, String mimetype) {
   String encoding = "UTF-8";
   try {
     if (in != null) {
       // The InputStream must support marks
       final BufferedInputStream bufferedInputStream = new BufferedInputStream(in);
       Charset charset =
           mimetypeService.getContentCharsetFinder().getCharset(bufferedInputStream, mimetype);
       encoding = charset.name();
     }
   } finally {
     IOUtils.closeQuietly(in);
   }
   return encoding;
 }
  /**
   * Streams content back to client from a given File.
   *
   * @param req The request
   * @param res The response
   * @param file The file whose content is to be streamed.
   * @param modifiedTime The modified datetime to use for the streamed content. If <tt>null</tt> the
   *     file's timestamp will be used.
   * @param attach Indicates whether the content should be streamed as an attachment or not
   * @param attachFileName Optional file name to use when attach is <code>true</code>
   * @throws IOException
   */
  public void streamContent(
      WebScriptRequest req,
      WebScriptResponse res,
      File file,
      Long modifiedTime,
      boolean attach,
      String attachFileName,
      Map<String, Object> model)
      throws IOException {
    if (logger.isDebugEnabled())
      logger.debug(
          "Retrieving content from file " + file.getAbsolutePath() + " (attach: " + attach + ")");

    // determine mimetype from file extension
    String filePath = file.getAbsolutePath();
    String mimetype = MimetypeMap.MIMETYPE_BINARY;
    int extIndex = filePath.lastIndexOf('.');
    if (extIndex != -1) {
      mimetype = mimetypeService.getMimetype(filePath.substring(extIndex + 1));
    }

    // setup file reader and stream
    FileContentReader reader = new FileContentReader(file);
    reader.setMimetype(mimetype);
    reader.setEncoding("UTF-8");

    long lastModified = modifiedTime == null ? file.lastModified() : modifiedTime;
    Date lastModifiedDate = new Date(lastModified);

    streamContentImpl(
        req,
        res,
        reader,
        null,
        null,
        attach,
        lastModifiedDate,
        String.valueOf(lastModifiedDate.getTime()),
        attachFileName,
        model);
  }
  /**
   * Stream content implementation
   *
   * @param req The request
   * @param res The response
   * @param reader The reader
   * @param nodeRef The content nodeRef if applicable
   * @param propertyQName The content property if applicable
   * @param attach Indicates whether the content should be streamed as an attachment or not
   * @param modified Modified date of content
   * @param eTag ETag to use
   * @param attachFileName Optional file name to use when attach is <code>true</code>
   * @throws IOException
   */
  public void streamContentImpl(
      WebScriptRequest req,
      WebScriptResponse res,
      ContentReader reader,
      final NodeRef nodeRef,
      final QName propertyQName,
      final boolean attach,
      final Date modified,
      String eTag,
      final String attachFileName,
      Map<String, Object> model)
      throws IOException {
    setAttachment(null, res, attach, attachFileName);

    // establish mimetype
    String mimetype = reader.getMimetype();
    String extensionPath = req.getExtensionPath();
    if (mimetype == null || mimetype.length() == 0) {
      mimetype = MimetypeMap.MIMETYPE_BINARY;
      int extIndex = extensionPath.lastIndexOf('.');
      if (extIndex != -1) {
        String ext = extensionPath.substring(extIndex + 1);
        mimetype = mimetypeService.getMimetype(ext);
      }
    }

    res.setHeader(HEADER_ACCEPT_RANGES, "bytes");
    try {
      boolean processedRange = false;
      String range = req.getHeader(HEADER_CONTENT_RANGE);
      final long size = reader.getSize();
      final String encoding = reader.getEncoding();

      if (attach) {
        final String finalMimetype = mimetype;

        eventPublisher.publishEvent(
            new EventPreparator() {
              @Override
              public Event prepareEvent(String user, String networkId, String transactionId) {
                String siteId = siteService.getSiteShortName(nodeRef);

                return new ContentEventImpl(
                    ContentEvent.DOWNLOAD,
                    user,
                    networkId,
                    transactionId,
                    nodeRef.getId(),
                    siteId,
                    propertyQName.toString(),
                    Client.webclient,
                    attachFileName,
                    finalMimetype,
                    size,
                    encoding);
              }
            });
      }

      if (range == null) {
        range = req.getHeader(HEADER_RANGE);
      }
      if (range != null) {
        if (logger.isDebugEnabled()) logger.debug("Found content range header: " + range);

        // ensure the range header is starts with "bytes=" and process the range(s)
        if (range.length() > 6) {
          if (range.indexOf(',') != -1 && (nodeRef == null || propertyQName == null)) {
            if (logger.isInfoEnabled()) logger.info("Multi-range only supported for nodeRefs");
          } else {
            HttpRangeProcessor rangeProcessor = new HttpRangeProcessor(contentService);
            processedRange =
                rangeProcessor.processRange(
                    res,
                    reader,
                    range.substring(6),
                    nodeRef,
                    propertyQName,
                    mimetype,
                    req.getHeader(HEADER_USER_AGENT));
          }
        }
      }
      if (processedRange == false) {
        if (logger.isDebugEnabled()) logger.debug("Sending complete file content...");

        // set mimetype for the content and the character encoding for the stream
        res.setContentType(mimetype);
        res.setContentEncoding(encoding);

        // return the complete entity range
        res.setHeader(
            HEADER_CONTENT_RANGE,
            "bytes 0-" + Long.toString(size - 1L) + "/" + Long.toString(size));
        res.setHeader(HEADER_CONTENT_LENGTH, Long.toString(size));

        // set caching
        setResponseCache(res, modified, eTag, model);

        // get the content and stream directly to the response output stream
        // assuming the repository is capable of streaming in chunks, this should allow large files
        // to be streamed directly to the browser response stream.
        reader.getContent(res.getOutputStream());
      }
    } catch (SocketException e1) {
      // the client cut the connection - our mission was accomplished apart from a little error
      // message
      if (logger.isInfoEnabled()) logger.info("Client aborted stream read:\n\tcontent: " + reader);
    } catch (ContentIOException e2) {
      if (logger.isInfoEnabled()) logger.info("Client aborted stream read:\n\tcontent: " + reader);
    }
  }
 @Override
 public String guessMimetype(Resource resource) {
   return mimetypeService.guessMimetype(resource.getFilename());
 }