/**
   * Returns paths constructed by rewriting pathInfo using rules from the hosted site's mappings.
   * Paths are ordered from most to least specific match.
   *
   * @param site The hosted site.
   * @param pathInfo Path to be rewritten.
   * @param queryString
   * @return The rewritten path. May be either:
   *     <ul>
   *       <li>A path to a file in the Orion workspace, eg. <code>/ProjectA/foo/bar.txt</code>
   *       <li>An absolute URL pointing to another site, eg. <code>http://foo.com/bar.txt</code>
   *     </ul>
   *
   * @return The rewritten paths.
   * @throws URISyntaxException
   */
  private URI[] getMapped(IHostedSite site, IPath pathInfo, String queryString)
      throws URISyntaxException {
    final Map<String, List<String>> map = site.getMappings();
    final IPath originalPath = pathInfo;
    IPath path = originalPath.removeTrailingSeparator();

    List<URI> uris = new ArrayList<URI>();
    String rest = null;
    final int count = path.segmentCount();
    for (int i = 0; i <= count; i++) {
      List<String> base = map.get(path.toString());
      if (base != null) {
        rest = originalPath.removeFirstSegments(count - i).toString();
        for (int j = 0; j < base.size(); j++) {
          URI uri =
              rest.equals("") ? new URI(base.get(j)) : URIUtil.append(new URI(base.get(j)), rest);
          uris.add(
              new URI(
                  uri.getScheme(),
                  uri.getUserInfo(),
                  uri.getHost(),
                  uri.getPort(),
                  uri.getPath(),
                  queryString,
                  uri.getFragment()));
        }
      }
      path = path.removeLastSegments(1);
    }
    if (uris.size() == 0)
      // No mapping for /
      return null;
    else return uris.toArray(new URI[uris.size()]);
  }
 private void handleOpenArticle(
     Request request, HttpServletResponse httpServletResponse, String target) throws Exception {
   try {
     int k1 = target.indexOf('/', 1);
     int k2 = target.indexOf('/', k1 + 1);
     String feedId = target.substring(k1 + 1, k2);
     String strSeq = target.substring(k2 + 1);
     int seq = Integer.parseInt(strSeq);
     Article article = articleDb.get(feedId, seq);
     LoginInfo loginInfo = userHelpers.getLoginInfo(request);
     // ttt2 using the link from a non-authenticated browser causes a NPE; maybe do something
     // better, e.g. sign up
     ReadArticlesColl readArticlesColl = readArticlesCollDb.get(loginInfo.userId, feedId);
     if (readArticlesColl == null) {
       readArticlesColl = new ReadArticlesColl(loginInfo.userId, feedId);
     }
     if (!readArticlesColl.isRead(seq)) {
       readArticlesColl.markRead(seq, Config.getConfig().maxSizeForReadArticles);
       readArticlesCollDb.add(readArticlesColl);
     }
     String s =
         URIUtil.encodePath(article.url)
             .replace("%3F", "?")
             .replace("%23", "#"); // ttt2 see how to do this right
     httpServletResponse.sendRedirect(s);
   } catch (Exception e) {
     WebUtils.showResult(
         String.format("Failed to get article for path %s. %s", target, e),
         "/",
         request,
         httpServletResponse);
   }
 }