/** @inheritDoc */
 public long getCommentCount(Weblog website) throws WebloggerException {
   TypedQuery<Long> q =
       strategy.getNamedQuery("WeblogEntryComment.getCountDistinctByWebsite&Status", Long.class);
   q.setParameter(1, website);
   q.setParameter(2, ApprovalStatus.APPROVED);
   return q.getResultList().get(0);
 }
  /** @inheritDoc */
  public String createAnchor(WeblogEntry entry) throws WebloggerException {
    // Check for uniqueness of anchor
    String base = entry.createAnchorBase();
    String name = base;
    int count = 0;

    while (true) {
      if (count > 0) {
        name = base + count;
      }

      TypedQuery<WeblogEntry> q =
          strategy.getNamedQuery("WeblogEntry.getByWebsite&Anchor", WeblogEntry.class);
      q.setParameter(1, entry.getWebsite());
      q.setParameter(2, name);
      List results = q.getResultList();

      if (results.size() < 1) {
        break;
      } else {
        count++;
      }
    }
    return name;
  }
 /** @inheritDoc */
 public long getEntryCount(Weblog website) throws WebloggerException {
   TypedQuery<Long> q =
       strategy.getNamedQuery("WeblogEntry.getCountDistinctByStatus&Website", Long.class);
   q.setParameter(1, PubStatus.PUBLISHED);
   q.setParameter(2, website);
   return q.getResultList().get(0);
 }
  /** @inheritDoc */
  public void incrementHitCount(Weblog weblog, int amount) throws WebloggerException {

    if (amount == 0) {
      throw new WebloggerException("Tag increment amount cannot be zero.");
    }

    if (weblog == null) {
      throw new WebloggerException("Website cannot be NULL.");
    }

    TypedQuery<WeblogHitCount> q =
        strategy.getNamedQuery("WeblogHitCount.getByWeblog", WeblogHitCount.class);
    q.setParameter(1, weblog);
    WeblogHitCount hitCount;
    try {
      hitCount = q.getSingleResult();
    } catch (NoResultException e) {
      hitCount = null;
    }

    // create it if it doesn't exist
    if (hitCount == null && amount > 0) {
      hitCount = new WeblogHitCount();
      hitCount.setWeblog(weblog);
      hitCount.setDailyHits(amount);
      strategy.store(hitCount);
    } else if (hitCount != null) {
      hitCount.setDailyHits(hitCount.getDailyHits() + amount);
      strategy.store(hitCount);
    }
  }
 /** @inheritDoc */
 public boolean isWeblogCategoryInUse(WeblogCategory cat) throws WebloggerException {
   if (cat.getWeblog().getBloggerCategory().equals(cat)) {
     return true;
   }
   TypedQuery<WeblogEntry> q =
       strategy.getNamedQuery("WeblogEntry.getByCategory", WeblogEntry.class);
   q.setParameter(1, cat);
   int entryCount = q.getResultList().size();
   return entryCount > 0;
 }
 /** @inheritDoc */
 public WeblogHitCount getHitCountByWeblog(Weblog weblog) throws WebloggerException {
   TypedQuery<WeblogHitCount> q =
       strategy.getNamedQuery("WeblogHitCount.getByWeblog", WeblogHitCount.class);
   q.setParameter(1, weblog);
   try {
     return q.getSingleResult();
   } catch (NoResultException e) {
     return null;
   }
 }
 /** @inheritDoc */
 public List<WeblogEntry> getWeblogEntriesPinnedToMain(Integer max) throws WebloggerException {
   TypedQuery<WeblogEntry> query =
       strategy.getNamedQuery(
           "WeblogEntry.getByPinnedToMain&statusOrderByPubTimeDesc", WeblogEntry.class);
   query.setParameter(1, Boolean.TRUE);
   query.setParameter(2, PubStatus.PUBLISHED);
   if (max != null) {
     query.setMaxResults(max);
   }
   return query.getResultList();
 }
  /** @inheritDoc */
  @Override
  public List<WeblogCategory> getWeblogCategories(Weblog website) throws WebloggerException {
    if (website == null) {
      throw new WebloggerException("website is null");
    }

    TypedQuery<WeblogCategory> q =
        strategy.getNamedQuery("WeblogCategory.getByWeblog", WeblogCategory.class);
    q.setParameter(1, website);
    return q.getResultList();
  }
 /** @inheritDoc */
 public WeblogCategory getWeblogCategoryByName(Weblog weblog, String categoryName)
     throws WebloggerException {
   TypedQuery<WeblogCategory> q =
       strategy.getNamedQuery("WeblogCategory.getByWeblog&Name", WeblogCategory.class);
   q.setParameter(1, weblog);
   q.setParameter(2, categoryName);
   try {
     return q.getSingleResult();
   } catch (NoResultException e) {
     return null;
   }
 }
 /** @inheritDoc */
 public void resetHitCount(Weblog weblog) throws WebloggerException {
   TypedQuery<WeblogHitCount> q =
       strategy.getNamedQuery("WeblogHitCount.getByWeblog", WeblogHitCount.class);
   q.setParameter(1, weblog);
   WeblogHitCount hitCount;
   try {
     hitCount = q.getSingleResult();
     hitCount.setDailyHits(0);
     strategy.store(hitCount);
   } catch (NoResultException e) {
     // ignore: no hit count for weblog
   }
 }
  /** @inheritDoc */
  public WeblogEntry getWeblogEntryByAnchor(Weblog website, String anchor)
      throws WebloggerException {

    if (website == null) {
      throw new WebloggerException("Website is null");
    }

    if (anchor == null) {
      throw new WebloggerException("Anchor is null");
    }

    // mapping key is combo of weblog + anchor
    String mappingKey = website.getHandle() + ":" + anchor;

    // check cache first
    // NOTE: if we ever allow changing anchors then this needs updating
    if (this.entryAnchorToIdMap.containsKey(mappingKey)) {

      WeblogEntry entry = this.getWeblogEntry(this.entryAnchorToIdMap.get(mappingKey));
      if (entry != null) {
        LOG.debug("entryAnchorToIdMap CACHE HIT - " + mappingKey);
        return entry;
      } else {
        // mapping hit with lookup miss?  mapping must be old, remove it
        this.entryAnchorToIdMap.remove(mappingKey);
      }
    }

    // cache failed, do lookup
    TypedQuery<WeblogEntry> q =
        strategy.getNamedQuery(
            "WeblogEntry.getByWebsite&AnchorOrderByPubTimeDesc", WeblogEntry.class);
    q.setParameter(1, website);
    q.setParameter(2, anchor);
    WeblogEntry entry;
    try {
      entry = q.getSingleResult();
    } catch (NoResultException e) {
      entry = null;
    }

    // add mapping to cache
    if (entry != null) {
      LOG.debug("entryAnchorToIdMap CACHE MISS - " + mappingKey);
      this.entryAnchorToIdMap.put(mappingKey, entry.getId());
    }
    return entry;
  }
  /** @inheritDoc */
  public List<WeblogHitCount> getHotWeblogs(int sinceDays, int offset, int length)
      throws WebloggerException {

    // figure out start date
    Calendar cal = Calendar.getInstance();
    cal.setTime(new Date());
    cal.add(Calendar.DATE, -1 * sinceDays);
    Date startDate = cal.getTime();

    TypedQuery<WeblogHitCount> query =
        strategy.getNamedQuery(
            "WeblogHitCount.getByWeblogEnabledTrueAndActiveTrue&DailyHitsGreaterThenZero&WeblogLastModifiedGreaterOrderByDailyHitsDesc",
            WeblogHitCount.class);
    query.setParameter(1, startDate);

    if (offset != 0) {
      query.setFirstResult(offset);
    }
    if (length != -1) {
      query.setMaxResults(length);
    }
    return query.getResultList();
  }
  /** @inheritDoc */
  public List<TagStat> getPopularTags(Weblog website, Date startDate, int offset, int limit)
      throws WebloggerException {
    TypedQuery<TagStat> query;
    List queryResults;

    if (website != null) {
      if (startDate != null) {
        Timestamp start = new Timestamp(startDate.getTime());
        query =
            strategy.getNamedQuery(
                "WeblogEntryTagAggregate.getPopularTagsByWebsite&StartDate", TagStat.class);
        query.setParameter(1, website);
        query.setParameter(2, start);
      } else {
        query =
            strategy.getNamedQuery(
                "WeblogEntryTagAggregate.getPopularTagsByWebsite", TagStat.class);
        query.setParameter(1, website);
      }
    } else {
      if (startDate != null) {
        Timestamp start = new Timestamp(startDate.getTime());
        query =
            strategy.getNamedQuery(
                "WeblogEntryTagAggregate.getPopularTagsByWebsiteNull&StartDate", TagStat.class);
        query.setParameter(1, start);
      } else {
        query =
            strategy.getNamedQuery(
                "WeblogEntryTagAggregate.getPopularTagsByWebsiteNull", TagStat.class);
      }
    }
    if (offset != 0) {
      query.setFirstResult(offset);
    }
    if (limit != -1) {
      query.setMaxResults(limit);
    }
    queryResults = query.getResultList();

    double min = Integer.MAX_VALUE;
    double max = Integer.MIN_VALUE;

    List<TagStat> results = new ArrayList<TagStat>(limit >= 0 ? limit : 25);

    if (queryResults != null) {
      for (Object obj : queryResults) {
        Object[] row = (Object[]) obj;
        TagStat t = new TagStat();
        t.setName((String) row[0]);
        t.setCount(((Number) row[1]).intValue());

        min = Math.min(min, t.getCount());
        max = Math.max(max, t.getCount());
        results.add(t);
      }
    }

    min = Math.log(1 + min);
    max = Math.log(1 + max);

    double range = Math.max(.01, max - min) * 1.0001;

    for (TagStat t : results) {
      t.setIntensity((int) (1 + Math.floor(5 * (Math.log(1 + t.getCount()) - min) / range)));
    }

    // sort results by name, because query had to sort by total
    Collections.sort(results, TAG_STAT_NAME_COMPARATOR);

    return results;
  }
  /** @inheritDoc */
  public List<StatCount> getMostCommentedWeblogEntries(
      Weblog website, Date startDate, Date endDate, int offset, int length)
      throws WebloggerException {
    TypedQuery<WeblogEntryComment> query;
    List queryResults;

    Timestamp end = new Timestamp(endDate != null ? endDate.getTime() : new Date().getTime());

    if (website != null) {
      if (startDate != null) {
        Timestamp start = new Timestamp(startDate.getTime());
        query =
            strategy.getNamedQuery(
                "WeblogEntryComment.getMostCommentedWeblogEntryByWebsite&EndDate&StartDate",
                WeblogEntryComment.class);
        query.setParameter(1, website);
        query.setParameter(2, end);
        query.setParameter(3, start);
      } else {
        query =
            strategy.getNamedQuery(
                "WeblogEntryComment.getMostCommentedWeblogEntryByWebsite&EndDate",
                WeblogEntryComment.class);
        query.setParameter(1, website);
        query.setParameter(2, end);
      }
    } else {
      if (startDate != null) {
        Timestamp start = new Timestamp(startDate.getTime());
        query =
            strategy.getNamedQuery(
                "WeblogEntryComment.getMostCommentedWeblogEntryByEndDate&StartDate",
                WeblogEntryComment.class);
        query.setParameter(1, end);
        query.setParameter(2, start);
      } else {
        query =
            strategy.getNamedQuery(
                "WeblogEntryComment.getMostCommentedWeblogEntryByEndDate",
                WeblogEntryComment.class);
        query.setParameter(1, end);
      }
    }
    if (offset != 0) {
      query.setFirstResult(offset);
    }
    if (length != -1) {
      query.setMaxResults(length);
    }
    queryResults = query.getResultList();
    List<StatCount> results = new ArrayList<StatCount>();
    if (queryResults != null) {
      for (Object obj : queryResults) {
        Object[] row = (Object[]) obj;
        StatCount sc =
            new StatCount(
                (String) row[1], // weblog handle
                (String) row[2], // entry anchor
                (String) row[3], // entry title
                "statCount.weblogEntryCommentCountType", // stat desc
                ((Long) row[0])); // count
        sc.setWeblogHandle((String) row[1]);
        results.add(sc);
      }
    }
    // Original query ordered by desc count.
    // JPA QL doesn't allow queries to be ordered by agregates; do it in memory
    Collections.sort(results, STAT_COUNT_COUNT_REVERSE_COMPARATOR);

    return results;
  }
  /**
   * This method maintains the tag aggregate table up-to-date with total counts. More specifically
   * every time this method is called it will act upon exactly two rows in the database
   * (tag,website,count), one with website matching the argument passed and one where website is
   * null. If the count ever reaches zero, the row must be deleted.
   *
   * @param name The tag name
   * @param website The website to used when updating the stats.
   * @param amount The amount to increment the tag count (it can be positive or negative).
   * @throws WebloggerException
   */
  private void updateTagCount(String name, Weblog website, int amount) throws WebloggerException {
    if (amount == 0) {
      throw new WebloggerException("Tag increment amount cannot be zero.");
    }

    if (website == null) {
      throw new WebloggerException("Website cannot be NULL.");
    }

    // The reason why add order lastUsed desc is to make sure we keep picking the most recent
    // one in the case where we have multiple rows (clustered environment)
    // eventually that second entry will have a very low total (most likely 1) and
    // won't matter
    TypedQuery<WeblogEntryTagAggregate> weblogQuery =
        strategy.getNamedQuery(
            "WeblogEntryTagAggregate.getByName&WebsiteOrderByLastUsedDesc",
            WeblogEntryTagAggregate.class);
    weblogQuery.setParameter(1, name);
    weblogQuery.setParameter(2, website);
    WeblogEntryTagAggregate weblogTagData;
    try {
      weblogTagData = weblogQuery.getSingleResult();
    } catch (NoResultException e) {
      weblogTagData = null;
    }

    TypedQuery<WeblogEntryTagAggregate> siteQuery =
        strategy.getNamedQuery(
            "WeblogEntryTagAggregate.getByName&WebsiteNullOrderByLastUsedDesc",
            WeblogEntryTagAggregate.class);
    siteQuery.setParameter(1, name);
    WeblogEntryTagAggregate siteTagData;
    try {
      siteTagData = siteQuery.getSingleResult();
    } catch (NoResultException e) {
      siteTagData = null;
    }
    Timestamp lastUsed = new Timestamp((new Date()).getTime());

    // create it only if we are going to need it.
    if (weblogTagData == null && amount > 0) {
      weblogTagData = new WeblogEntryTagAggregate(null, website, name, amount);
      weblogTagData.setLastUsed(lastUsed);
      strategy.store(weblogTagData);

    } else if (weblogTagData != null) {
      weblogTagData.setTotal(weblogTagData.getTotal() + amount);
      weblogTagData.setLastUsed(lastUsed);
      strategy.store(weblogTagData);
    }

    // create it only if we are going to need it.
    if (siteTagData == null && amount > 0) {
      siteTagData = new WeblogEntryTagAggregate(null, null, name, amount);
      siteTagData.setLastUsed(lastUsed);
      strategy.store(siteTagData);

    } else if (siteTagData != null) {
      siteTagData.setTotal(siteTagData.getTotal() + amount);
      siteTagData.setLastUsed(lastUsed);
      strategy.store(siteTagData);
    }

    // delete all bad counts
    Query removeq = strategy.getNamedUpdate("WeblogEntryTagAggregate.removeByTotalLessEqual");
    removeq.setParameter(1, 0);
    removeq.executeUpdate();
  }