/**
   * Returns a collection of Attachments, containing each and every attachment that is in this Wiki.
   *
   * @return A collection of attachments. If attachments are disabled, will return an empty
   *     collection.
   * @throws ProviderException If something went wrong with the backend
   */
  public Collection getAllAttachments() throws ProviderException {
    if (attachmentsEnabled()) {
      return m_provider.listAllChanged(new Date(0L));
    }

    return new ArrayList<Attachment>();
  }
  // FIXME: Should also use events!
  public void deleteAttachment(Attachment att) throws ProviderException {
    if (m_provider == null) return;

    m_provider.deleteAttachment(att);

    m_engine.getSearchManager().pageRemoved(att);

    m_engine.getReferenceManager().clearPageEntries(att.getName());
  }
  /**
   * Returns an attachment stream using the particular WikiContext. This method should be used
   * instead of getAttachmentStream(Attachment), since it also allows the DynamicAttachments to
   * function.
   *
   * @param ctx The Wiki Context
   * @param att The Attachment to find
   * @return An InputStream. May return null, if attachments are disabled. You must take care of
   *     closing it.
   * @throws ProviderException If the backend fails due to some reason
   * @throws IOException If the stream cannot be opened
   */
  public InputStream getAttachmentStream(WikiContext ctx, Attachment att)
      throws ProviderException, IOException {
    if (m_provider == null) {
      return null;
    }

    if (att instanceof DynamicAttachment) {
      return ((DynamicAttachment) att).getProvider().getAttachmentData(ctx, att);
    }

    return m_provider.getAttachmentData(att);
  }
  /**
   * Returns a list of versions of the attachment.
   *
   * @param attachmentName A fully qualified name of the attachment.
   * @return A list of Attachments. May return null, if attachments are disabled.
   * @throws ProviderException If the provider fails for some reason.
   */
  public List getVersionHistory(String attachmentName) throws ProviderException {
    if (m_provider == null) {
      return null;
    }

    Attachment att = getAttachmentInfo((WikiContext) null, attachmentName);

    if (att != null) {
      return m_provider.getVersionHistory(att);
    }

    return null;
  }
  // FIXME: Perhaps this should fail somehow.
  public AttachmentManager(WikiEngine engine, Properties props) {
    String classname;

    m_engine = engine;

    //
    //  If user wants to use a cache, then we'll use the CachingProvider.
    //
    boolean useCache = "true".equals(props.getProperty(PageManager.PROP_USECACHE));

    if (useCache) {
      classname = "org.apache.wiki.providers.CachingAttachmentProvider";
    } else {
      classname = props.getProperty(PROP_PROVIDER);
    }

    //
    //  If no class defined, then will just simply fail.
    //
    if (classname == null) {
      log.info("No attachment provider defined - disabling attachment support.");
      return;
    }

    //
    //  Create and initialize the provider.
    //
    try {
      Class<?> providerclass = ClassUtil.findClass("org.apache.wiki.providers", classname);

      m_provider = (WikiAttachmentProvider) providerclass.newInstance();

      m_provider.initialize(m_engine, props);
    } catch (ClassNotFoundException e) {
      log.error("Attachment provider class not found", e);
    } catch (InstantiationException e) {
      log.error("Attachment provider could not be created", e);
    } catch (IllegalAccessException e) {
      log.error("You may not access the attachment provider class", e);
    } catch (NoRequiredPropertyException e) {
      log.error("Attachment provider did not find a property that it needed: " + e.getMessage(), e);
      m_provider = null; // No, it did not work.
    } catch (IOException e) {
      log.error("Attachment provider reports IO error", e);
      m_provider = null;
    }
  }
  // FIXME: This API should be changed to return a List.
  @SuppressWarnings("unchecked")
  public Collection listAttachments(WikiPage wikipage) throws ProviderException {
    if (m_provider == null) {
      return new ArrayList();
    }

    Collection atts = m_provider.listAttachments(wikipage);

    //
    //  This is just a sanity check; all of our providers return a Collection.
    //
    if (atts instanceof List) {
      m_engine.getPageSorter().sortPages((List) atts);
    }

    return atts;
  }
  /**
   * Stores an attachment directly from a stream. If the attachment did not exist previously, this
   * method will create it. If it did exist, it stores a new version.
   *
   * @param att Attachment to store this under.
   * @param in InputStream from which the attachment contents will be read.
   * @throws IOException If writing the attachment failed.
   * @throws ProviderException If something else went wrong.
   */
  public void storeAttachment(Attachment att, InputStream in)
      throws IOException, ProviderException {
    if (m_provider == null) {
      return;
    }

    //
    //  Checks if the actual, real page exists without any modifications
    //  or aliases.  We cannot store an attachment to a non-existant page.
    //
    if (!m_engine.getPageManager().pageExists(att.getParentName())) {
      // the caller should catch the exception and use the exception text as an i18n key
      throw new ProviderException("attach.parent.not.exist");
    }

    m_provider.putAttachmentData(att, in);

    m_engine.getReferenceManager().updateReferences(att.getName(), new java.util.Vector());

    WikiPage parent = new WikiPage(m_engine, att.getParentName());
    m_engine.updateReferences(parent);

    m_engine.getSearchManager().reindexPage(att);
  }
  /**
   * Deletes the given attachment version.
   *
   * @param att The attachment to delete
   * @throws ProviderException If something goes wrong with the backend.
   */
  public void deleteVersion(Attachment att) throws ProviderException {
    if (m_provider == null) return;

    m_provider.deleteVersion(att);
  }
  /**
   * Figures out the full attachment name from the context and attachment name.
   *
   * @param context The current WikiContext
   * @param attachmentname The file name of the attachment.
   * @param version A particular version.
   * @return Attachment, or null, if no such attachment or version exists.
   * @throws ProviderException If something goes wrong.
   */
  public Attachment getAttachmentInfo(WikiContext context, String attachmentname, int version)
      throws ProviderException {
    if (m_provider == null) {
      return null;
    }

    WikiPage currentPage = null;

    if (context != null) {
      currentPage = context.getPage();
    }

    //
    //  Figure out the parent page of this attachment.  If we can't find it,
    //  we'll assume this refers directly to the attachment.
    //
    int cutpt = attachmentname.lastIndexOf('/');

    if (cutpt != -1) {
      String parentPage = attachmentname.substring(0, cutpt);
      parentPage = MarkupParser.cleanLink(parentPage);
      attachmentname = attachmentname.substring(cutpt + 1);

      // If we for some reason have an empty parent page name;
      // this can't be an attachment
      if (parentPage.length() == 0) return null;

      currentPage = m_engine.getPage(parentPage);

      //
      // Go check for legacy name
      //
      // FIXME: This should be resolved using CommandResolver,
      //        not this adhoc way.  This also assumes that the
      //        legacy charset is a subset of the full allowed set.
      if (currentPage == null) {
        currentPage = m_engine.getPage(MarkupParser.wikifyLink(parentPage));
      }
    }

    //
    //  If the page cannot be determined, we cannot possibly find the
    //  attachments.
    //
    if (currentPage == null || currentPage.getName().length() == 0) {
      return null;
    }

    // System.out.println("Seeking info on "+currentPage+"::"+attachmentname);

    //
    //  Finally, figure out whether this is a real attachment or a generated
    //  attachment.
    //
    Attachment att;

    att = getDynamicAttachment(currentPage.getName() + "/" + attachmentname);

    if (att == null) {
      att = m_provider.getAttachmentInfo(currentPage, attachmentname, version);
    }

    return att;
  }