public String getPositionAndAffiliationString(int limit, int additionalLength, String tail) {
    if (null == tail) {
      tail = "...";
    }
    StringBuilder sb = new StringBuilder();
    if (!Strings.isEmpty(this.get_sPosition())) {
      sb.append(this.get_sPosition());
    }
    if (!Strings.isEmpty(this.get_sPosition()) && !Strings.isEmpty(this.get_sAffiliation())) {
      sb.append(", ");
    }
    if (!Strings.isEmpty(this.get_sAffiliation())) {
      sb.append(this.get_sAffiliation());
    }
    int max = limit - additionalLength;

    // capitalize
    if (sb.length() > 0) {
      sb.replace(0, 1, new Character(sb.charAt(0)).toString().toUpperCase());
    }

    String result = null;
    if (sb.length() > max) {
      result = sb.substring(0, max) + tail;
    } else {
      result = sb.toString();
    }
    return result;
  }
  @Published()
  @GET
  @Path("/jconf/{jconfName: .*}")
  @Produces({"text/html", "text/javascript", "application/json"})
  public String getPublicationsByJconfName(
      @DefaultValue("") @PathParam("jconfName") String jconfName,
      @DefaultValue("-1") @QueryParam("startYear") Integer startYear,
      @DefaultValue("-1") @QueryParam("endYear") Integer endYear,
      @DefaultValue("0") @QueryParam("limStart") Integer limStart,
      @DefaultValue("-1") @QueryParam("limEnd") Integer limEnd) {

    // Check permission.
    String denyReason = support.checkPermission(request, uriInfo);
    if (null != denyReason) {
      return denyReason;
    }

    try {
      Jconf jconf = conferenceService.getConference(Strings.replace_back(jconfName));
      if (jconf != null) {
        return getPublicationsByJconfID(jconf.getId(), startYear, endYear, limStart, limEnd);
      } else {
        return "[]";
      }
    } catch (Throwable t) {
      t.printStackTrace();
      return support.throwJson(t.getMessage(), t);
    }
  }
  /**
   * 专家的Image地址由好多,这里按优先顺序取得人物头像地址。
   *
   * @param imgType IMG_SIZE_ORI 原图,小图和中图。 @TODO Move this out.
   */
  public String getDisplayImage(int imgType) {
    // FIXME can't different self upload image and downloaded image. ugly
    // code.
    // --user upload picture.

    if (null != this._sImgURL) {
      String trimed = this._sImgURL.trim();
      if (trimed.startsWith("http://")
          && trimed.contains("arnetminer")
          && trimed.contains("/upload/")) {
        return _sImgURL;
      }
    }
    if (!Strings.isEmpty(imgName)) {
      if (imgName.toLowerCase().startsWith("http")) {
        // special, replace to new imgname;
        if (imgName.startsWith("http://arnetminer.org/pictures")) {
          return PageSpeedUtil.smartAppendPicturePrefix(
              imgName.replace("http://arnetminer.org/pictures", ""));
        }
        return imgName;
      }
      if (imgType == IMG_SIZE_SMALL) {
        return PageSpeedUtil.smartAppendPicturePrefix(res.picture_thumbnail + imgName);
      } else if (imgType == IMG_SIZE_MEDIUM) {
        return PageSpeedUtil.smartAppendPicturePrefix(res.picture_original + imgName);
      } else {
        return PageSpeedUtil.smartAppendPicturePrefix(res.picture_original + imgName);
      }
    } else if (!Strings.isEmpty(_sImgURL)
        && (_sImgURL.startsWith("http") || _sImgURL.startsWith("ftp"))) {
      return _sImgURL;
    } else if (!Strings.isEmpty(_sImgSrc)
        && (_sImgSrc.startsWith("http") || _sImgSrc.startsWith("ftp"))) { // deprecated
      return _sImgSrc;
    } else {
      return res.picture_url + res.picture_nopicture;
    }
  }
  @Published()
  @GET
  @Path("/{pubids: [0-9,]+}")
  @Produces({"text/html", "text/javascript", "application/json"})
  public String getPublicationByPubIDs(@DefaultValue("") @PathParam("pubids") String pubIDStr) {

    // Check permission.
    String denyReason = support.checkPermission(request, uriInfo);
    if (null != denyReason) {
      return denyReason;
    }

    String[] ids = pubIDStr.split(",");
    Set<Integer> pubIDSet = new LinkedHashSet<Integer>();
    for (String idstr : ids) pubIDSet.add(Integer.parseInt(idstr.trim()));
    try {
      boolean withAbs = false;
      String fieldSelectString = Strings.safeTrim(REQ.get(request, FIELDS));
      if (fieldSelectString.contains("+abs") || fieldSelectString.contains("abs")) {
        withAbs = true;
      }
      List<Publication> pubSet =
          publicationService.getPublications(withAbs, pubIDSet.toArray(new Integer[] {}));

      // if has html, return here.
      String htmlResult = support.returnHtml(request, "person_profile.vm", pubSet);
      if (null != htmlResult) {
        return htmlResult;
      }

      Publication[] parray = new Publication[pubSet.size()];
      parray = pubSet.toArray(parray);

      return _returnPublication(request, parray);
    } catch (Throwable t) {
      t.printStackTrace();
      return support.throwJson(t.getMessage(), t);
    }
  }
  /**
   * @param option default tff, [contactInfo, publicationList, coauthors]
   * @return
   */
  @Published()
  @GET
  @Path("/{pubid: [0-9]+}")
  @Produces({"text/html", "text/javascript", "application/json"})
  public String getPublicationByPubID(@DefaultValue("-1") @PathParam("pubid") Integer pubID) {

    // Check permission.
    String denyReason = support.checkPermission(request, uriInfo);
    if (null != denyReason) {
      return denyReason;
    }

    try {
      boolean withAbs = false;
      String fieldSelectString = Strings.safeTrim(REQ.get(request, FIELDS));
      if (fieldSelectString.contains("+abs") || fieldSelectString.contains("abs")) {
        withAbs = true;
      }
      Publication pub = null;
      if (withAbs) {
        pub = publicationService.getPublicationFull(pubID);
      } else {
        pub = publicationService.getPublication(pubID);
      }

      // if has html, return here.
      String htmlResult = support.returnHtml(request, "person_profile.vm", pub);
      if (null != htmlResult) {
        return htmlResult;
      }

      return _returnPublication(request, pub);
    } catch (Throwable t) {
      t.printStackTrace();
      return support.throwJson(t.getMessage(), t);
    }
  }
 public String getCleanedAffiliation() {
   return Strings.deepTrim(_sAffiliation);
 }
 public String getCleanedPosition() {
   return Strings.deepTrim(_sPosition);
 }