protected <STATE, STACKITEM> TraceItem<STATE, STACKITEM> traceToWithPruningStack(
     IPdaAdapter<STATE, STACKITEM> pda,
     Iterable<STATE> starts,
     Iterator<STACKITEM> stack,
     Predicate<STATE> matches,
     Predicate<STATE> canPass) {
   StackItem<STACKITEM> stackItem = createStack(stack);
   List<TraceItem<STATE, STACKITEM>> current = Lists.newArrayList();
   Set<STATE> visited = Sets.newHashSet(starts);
   TraceItem<STATE, STACKITEM> result = null;
   for (STATE start : starts) {
     TraceItem<STATE, STACKITEM> item = new TraceItem<STATE, STACKITEM>(null, start, stackItem);
     //			if (matches.apply(start))
     //				result = item;
     current.add(item);
   }
   int counter = stackItem.size() * -1;
   while (current.size() > 0 && counter < visited.size()) {
     List<TraceItem<STATE, STACKITEM>> newCurrent = Lists.newArrayList();
     for (TraceItem<STATE, STACKITEM> trace : current)
       for (STATE follower : pda.getFollowers(trace.state)) {
         if (matches.apply(follower)) {
           TraceItem<STATE, STACKITEM> found =
               new TraceItem<STATE, STACKITEM>(trace, follower, trace.stackitem);
           if (found.stackitem == null) return found;
           if (result == null || result.stackitem.size() > found.stackitem.size()) {
             result = found;
             counter = result.stackitem.size() * -1;
           } else if (result.stackitem.size() == found.stackitem.size()
               && result.size() > found.size()) {
             result = found;
             counter = result.stackitem.size() * -1;
           }
         }
         if (canPass.apply(follower)) {
           STACKITEM push = pda.getPush(follower);
           visited.add(follower);
           if (push != null) {
             StackItem<STACKITEM> pushed = trace.stackitem.push(push);
             newCurrent.add(new TraceItem<STATE, STACKITEM>(trace, follower, pushed));
           } else {
             STACKITEM pop = pda.getPop(follower);
             if (pop != null) {
               if (trace.stackitem != null && pop == trace.stackitem.peek()) {
                 StackItem<STACKITEM> popped = trace.stackitem.pop();
                 newCurrent.add(new TraceItem<STATE, STACKITEM>(trace, follower, popped));
               }
             } else
               newCurrent.add(new TraceItem<STATE, STACKITEM>(trace, follower, trace.stackitem));
           }
         }
       }
     current = newCurrent;
     counter++;
   }
   return result;
 }
Ejemplo n.º 2
0
  /**
   * @param resourceBody
   * @param fqn
   * @return
   */
  public List<IEObjectDescription> findAttributesWithPrefix(
      ResourceBody resourceBody, QualifiedName fqn) {
    // Must be configured for the resource containing resourceBody
    List<IEObjectDescription> result = Lists.newArrayList();

    // do meta lookup first as this is made fast via a cache and these are used more frequent
    // than other parameters (measured).
    // TODO: VERIFY that empty last segment matches ok
    // TODO: Make sure that length of match is same number of segments
    if (metaCache == null) cacheMetaParameters(resourceBody);
    String fqnLast = fqn.getLastSegment();
    for (String name : metaCache.keySet())
      if (name.startsWith(fqnLast)) result.add(metaCache.get(name));

    result.addAll(
        findAttributesWithGuard(
            resourceBody, fqn, null, Lists.<QualifiedName>newArrayList(), true));
    return result;
  }
Ejemplo n.º 3
0
  /**
   * Find an attribute being a DefinitionArgument, Property, or Parameter for the given type, or a
   * meta Property or Parameter defined for the type 'Type'.
   *
   * @param scopeDetermeningObject
   * @param fqn
   * @return
   */
  protected List<IEObjectDescription> findAttributes(
      EObject scopeDetermeningObject, QualifiedName fqn, PPImportedNamesAdapter importedNames) {
    List<IEObjectDescription> result = null;

    // do meta lookup first as this is made fast via a cache and these are used more frequent
    // than other parameters (measured).
    if (metaCache == null) cacheMetaParameters(scopeDetermeningObject);
    IEObjectDescription d = metaCache.get(fqn.getLastSegment());
    if (d == null)
      result =
          findAttributesWithGuard(
              scopeDetermeningObject,
              fqn,
              importedNames,
              Lists.<QualifiedName>newArrayList(),
              false);
    else result = Lists.newArrayList(d);
    return result;
  }
 public List<STATE> asList() {
   List<STATE> result = Lists.newArrayList();
   TraceItem<STATE, STACKITEM> current = this;
   while (current != null) {
     result.add(current.state);
     current = current.parent;
   }
   Collections.reverse(result);
   return result;
 }
 @Override
 public String toString() {
   List<String> result = Lists.newArrayList();
   StackItem<T> current = this;
   while (current != null) {
     if (current.value != null) result.add(current.value.toString());
     current = current.pop();
   }
   return Join.join(", ", result);
 }
Ejemplo n.º 6
0
 /**
  * Adjusts the list of found targets in accordance with the search path for the resource being
  * linked. This potentially resolves ambiguities (if found result is further away on the path).
  * May return more than one result, if more than one resolution exist with the same path index.
  *
  * @param targets
  * @return list of descriptions with lowest index.
  */
 private List<IEObjectDescription> searchPathAdjusted(List<IEObjectDescription> targets) {
   int minIdx = Integer.MAX_VALUE;
   List<IEObjectDescription> result = Lists.newArrayList();
   for (IEObjectDescription d : targets) {
     int idx = searchPath.searchIndexOf(d);
     if (idx < 0) continue; // not found, skip
     if (idx < minIdx) {
       minIdx = idx;
       result.clear(); // forget worse result
     }
     // only remember if equal to best found so far
     if (idx <= minIdx) result.add(d);
   }
   return result;
 }
/**
 * Verify that only instances that fail cause dependency failures. In other words, when run, this
 * test should show: passed = [f#1 f#3 g#1 g#3], failed = [f#2], skipped = [g#2]
 *
 * @author Cedric Beust <*****@*****.**>
 */
public class InstanceSkipSampleTest {

  private int m_n;
  public static List<String> m_list = Lists.newArrayList();

  @Factory(dataProvider = "dp")
  public InstanceSkipSampleTest(int n) {
    m_n = n;
  }

  @DataProvider
  public static Object[][] dp() {
    return new Object[][] {
      new Object[] {1}, new Object[] {2}, new Object[] {3},
    };
  }

  @Test
  public void f() {
    if (m_n == 2) throw new RuntimeException();
    log("f");
  }

  @Test(dependsOnMethods = "f")
  public void g() {
    log("g");
  }

  private void log(String s) {
    m_list.add(s + "#" + m_n);
  }

  @Override
  public String toString() {
    return "" + m_n;
  }
}
Ejemplo n.º 8
0
  protected List<IEObjectDescription> findExternal(
      EObject scopeDetermeningObject,
      QualifiedName fqn,
      PPImportedNamesAdapter importedNames,
      boolean prefixMatch,
      EClass... eClasses) {
    if (scopeDetermeningObject == null)
      throw new IllegalArgumentException("scope determening object is null");
    if (fqn == null) throw new IllegalArgumentException("name is null");
    if (eClasses == null || eClasses.length < 1)
      throw new IllegalArgumentException("eClass is null or empty");

    if (fqn.getSegmentCount() == 1 && "".equals(fqn.getSegment(0)))
      throw new IllegalArgumentException("FQN has one empty segment");

    // Not meaningful to record the fact that an Absolute reference was used as nothing
    // is named with an absolute FQN (i.e. it is only used to do lookup).
    final boolean absoluteFQN = fqn.getSegmentCount() > 0 && "".equals(fqn.getSegment(0));
    if (importedNames != null) importedNames.add(absoluteFQN ? fqn.skipFirst(1) : fqn);

    List<IEObjectDescription> targets = Lists.newArrayList();
    Resource scopeDetermeningResource = scopeDetermeningObject.eResource();

    if (scopeDetermeningResource != resource) {
      // This is a lookup in the perspective of some other resource
      IResourceDescriptions descriptionIndex =
          indexProvider.getResourceDescriptions(scopeDetermeningResource);
      IResourceDescription descr =
          descriptionIndex.getResourceDescription(scopeDetermeningResource.getURI());

      // GIVE UP (the system is performing a build clean).
      if (descr == null) return targets;
      QualifiedName nameOfScope = getNameOfScope(scopeDetermeningObject);

      // for(IContainer visibleContainer : manager.getVisibleContainers(descr, descriptionIndex)) {
      // for(EClass aClass : eClasses)
      for (IEObjectDescription objDesc :
          new NameInScopeFilter(
              prefixMatch,
              getExportedObjects(descr, descriptionIndex),
              // visibleContainer.getExportedObjects(),
              fqn,
              nameOfScope,
              eClasses)) targets.add(objDesc);
    } else {
      // This is lookup from the main resource perspective
      QualifiedName nameOfScope = getNameOfScope(scopeDetermeningObject);
      for (IEObjectDescription objDesc :
          new NameInScopeFilter(
              prefixMatch, //
              prefixMatch
                  ? exportedPerLastSegment.values()
                  : exportedPerLastSegment.get(fqn.getLastSegment()), //
              fqn,
              nameOfScope,
              eClasses)) targets.add(objDesc);
    }
    if (tracer.isTracing()) {
      for (IEObjectDescription d : targets)
        tracer.trace("    : ", converter.toString(d.getName()), " in: ", d.getEObjectURI().path());
    }
    return searchPathAdjusted(targets);
  }
  /*
   * 付款凭证: 客户+保证金充抵+租金
   */
  @Override
  public void createVoucherCautionMoney(
      ContractInfo contractInfo, JSONArray jsonArray, BigDecimal interest, BigDecimal cautionMoney)
      throws Exception {
    /** 第一步:建立基本凭证体集合和公用字段字段-开始 */
    List<IntereasVoucherHead> headObjList = new ArrayList<IntereasVoucherHead>(); // 凭证头集合
    List<IntereasVoucherEntries> bodyList = Lists.newArrayList(); // 凭证体集合
    String currentDate = DateUtil.getSystemDate(); // 当前时间
    User currentUser = SecurityUtil.getPrincipal(); // 当前登录人
    InterOrgCode interOrgCode =
        this.baseService.findEntityByID(InterOrgCode.class, "inter_orgcode_id_1"); // 所属公司
    DictionaryData voucherType =
        this.baseService.findEntityByID(DictionaryData.class, "voucher_type_1"); // 记账凭证
    // //凭证字
    DictionaryData currencyNumber =
        this.baseService.findEntityByID(DictionaryData.class, "currency_type1"); // 人民币
    Integer DR = 1; // 借
    Integer CR = -1; // 贷
    /** 公用字段-结束 */

    /**
     * ************************************ 分割线-以上不动
     * **********************************************************
     */

    /** 本凭证-公用字段-开始 */
    String workFlowName = "期末保证金抵扣流程"; // 流程名称
    // BigDecimal originalAmount = finstartdate.getStartMoney(); //原币金额
    /** 本凭证-公用字段-结束 */

    // 抵扣的每期租金
    for (int i = 0; i < jsonArray.length(); i++) {
      JSONObject jsonObj = jsonArray.getJSONObject(i);
      BigDecimal rent = new BigDecimal(jsonObj.getDouble("rent"));
      String planlist = jsonObj.optString("planlist");
      if (rent.compareTo(BigDecimal.ZERO) != 0) {

        /** 第一步:建立凭证体 */
        /** 借银行存款 */
        /**
         * ************************************ 凭证体1-分割线-开始
         * *******************************************************
         */

        // 其他应付款-保证金
        VoucherassStactsConfig config_01 =
            this.baseService.findEntityByID(VoucherassStactsConfig.class, "228"); // 凭证配置表
        // :其他应付款-保证金
        String voucherAbstract =
            contractInfo.getCustId().getCustName() + "保证金冲抵第" + planlist + "期租金"; // 摘要
        // 建立辅助账
        List<IntereasVoucherasStacts> stactsList_01 =
            voucherService.generateVoucherassStacts(
                contractInfo.getCustId(), null, config_01); // 辅助账集合

        /** 1 :建立凭证体1-开始 */
        IntereasVoucherEntries body_01 = new IntereasVoucherEntries();
        /**
         * 凭证体参数构建示例: 参数1:dictionaryData 币别数据字段对象 取:数据字典 人民币 currency_type1 || 美元 currency_type2
         * 参数2:num_entrySeq 凭证体分录行号 (德银暂时不传入该参数) 参数3:accountNumber 科目编码 ,已封装成对象 参数4:originalAmount
         * 原币金额 参数5:voucherAbstract 摘要 参数6:entryDC 借贷方向 (1 借方-1 贷方) 参数7:headObj 该凭证体对应的凭证头完整的实体对象信息
         */
        body_01.setAccountNumber(config_01); // 这个科目
        body_01.setOriginalAmount(rent); // 原币金额
        body_01.setEntryDC(DR); // 借贷方向 (DR(1) 借方- CR(1) 贷方)
        body_01.setVoucherAbstract(voucherAbstract); // 摘要
        body_01.setCurrencyNumber(currencyNumber); // 币别数据字段对象
        body_01.setIntereasVoucherasStactsInAction(stactsList_01); // 保存辅助账

        /** 建立凭证体1-结束 */
        bodyList.add(body_01);

        /**
         * ************************************ 凭证体1-分割线-结束
         * *******************************************************
         */

        /** 第一步:建立凭证体 */
        /** 长期应收款-应收融资租赁款-首付款-客户 */
        /**
         * ************************************ 凭证体2-分割线-开始
         * *******************************************************
         */

        // 其他应收款-本金进项税额
        VoucherassStactsConfig config_02 =
            this.baseService.findEntityByID(VoucherassStactsConfig.class, "225"); // 凭证配置表
        // 建立辅助账
        List<IntereasVoucherasStacts> stactsList_02 =
            voucherService.generateVoucherassStacts(
                contractInfo.getCustId(), null, config_02); // 辅助账集合

        // 税金
        // BigDecimal texEquipAmount = originalAmount.multiply(new BigDecimal(0.17)).setScale(2);
        // //金额乘以0.17

        /** 1 :建立凭证体1-开始 */
        IntereasVoucherEntries body_02 = new IntereasVoucherEntries();
        /**
         * 凭证体参数构建示例: 参数1:dictionaryData 币别数据字段对象 取:数据字典 人民币 currency_type1 || 美元 currency_type2
         * 参数2:num_entrySeq 凭证体分录行号 (德银暂时不传入该参数) 参数3:accountNumber 科目编码 ,已封装成对象 参数4:originalAmount
         * 原币金额 参数5:voucherAbstract 摘要 参数6:entryDC 借贷方向 (1 借方-1 贷方) 参数7:headObj 该凭证体对应的凭证头完整的实体对象信息
         */
        body_02.setAccountNumber(config_02); // 这个科目
        body_02.setOriginalAmount(rent); // 原币金额
        body_02.setEntryDC(CR); // 借贷方向 (DR(1) 借方- CR(1) 贷方)
        body_02.setVoucherAbstract(voucherAbstract); // 摘要
        body_02.setCurrencyNumber(currencyNumber); // 币别数据字段对象
        body_02.setIntereasVoucherasStactsInAction(stactsList_02); // 保存辅助账

        /** 建立凭证体1-结束 */
        bodyList.add(body_02);
      }
    }

    //		//抵扣的每期租金
    //		for(int i=0;i<jsonArray.length();i++){
    //			JSONObject jsonObj = jsonArray.getJSONObject(i);
    //			BigDecimal rent=new BigDecimal(jsonObj.getDouble("rent"));
    //			String planlist=jsonObj.optString("planlist");
    //			if(rent.compareTo(BigDecimal.ZERO)!=0){
    //				String
    // voucherAbstractrent="保证金抵充"+contractInfo.getContractNumber()+"-"+contractInfo.getCustId().getCustName()+"第"+planlist+"租金";//摘要
    //				/** 第一步:建立凭证体 */
    //				/** 预收账款-客户 */
    //				/************************************** 凭证体3-分割线-开始
    // ********************************************************/
    //				// 预收账款
    //				VoucherassStactsConfig config_03 =
    // this.baseService.findEntityByID(VoucherassStactsConfig.class, "225"); // 凭证配置表
    //				// 建立辅助账
    //				List<IntereasVoucherasStacts> stactsList_03 =
    // voucherService.generateVoucherassStacts(contractInfo.getCustId(), null, config_02); // 辅助账集合
    //				/** 1 :建立凭证体1-开始 */
    //				IntereasVoucherEntries body_03 = new IntereasVoucherEntries();
    //				/**
    //				 * 凭证体参数构建示例: 参数1:dictionaryData 币别数据字段对象 取:数据字典 人民币 currency_type1 || 美元 currency_type2
    // 参数2:num_entrySeq
    //				 * 凭证体分录行号 (德银暂时不传入该参数) 参数3:accountNumber 科目编码 ,已封装成对象 参数4:originalAmount 原币金额
    // 参数5:voucherAbstract 摘要
    //				 * 参数6:entryDC 借贷方向 (1 借方-1 贷方) 参数7:headObj 该凭证体对应的凭证头完整的实体对象信息
    //				 */
    //				body_03.setAccountNumber(config_03); // 这个科目
    //				body_03.setOriginalAmount(rent); // 原币金额
    //				body_03.setEntryDC(DR); // 借贷方向 (DR(1) 借方- CR(1) 贷方)
    //				body_03.setVoucherAbstract(voucherAbstractrent); // 摘要
    //				body_03.setCurrencyNumber(currencyNumber); // 币别数据字段对象
    //				body_03.setIntereasVoucherasStactsInAction(stactsList_03); // 保存辅助账
    //				/** 建立凭证体3-结束 */
    //				bodyList.add(body_03);
    //
    //				/** 第一步:建立凭证体 */
    //				/** 长期应收款-应收融资租赁款-租金-客户 */
    //				/************************************** 凭证体4-分割线-开始
    // ********************************************************/
    //				// 预收账款
    //				VoucherassStactsConfig config_04 =
    // this.baseService.findEntityByID(VoucherassStactsConfig.class, "222"); // 凭证配置表
    //				// 建立辅助账
    //				List<IntereasVoucherasStacts> stactsList_04 =
    // voucherService.generateVoucherassStacts(contractInfo.getCustId(), null, config_02); // 辅助账集合
    //				/** 1 :建立凭证体1-开始 */
    //				IntereasVoucherEntries body_04 = new IntereasVoucherEntries();
    //				/**
    //				 * 凭证体参数构建示例: 参数1:dictionaryData 币别数据字段对象 取:数据字典 人民币 currency_type1 || 美元 currency_type2
    // 参数2:num_entrySeq
    //				 * 凭证体分录行号 (德银暂时不传入该参数) 参数3:accountNumber 科目编码 ,已封装成对象 参数4:originalAmount 原币金额
    // 参数5:voucherAbstract 摘要
    //				 * 参数6:entryDC 借贷方向 (1 借方-1 贷方) 参数7:headObj 该凭证体对应的凭证头完整的实体对象信息
    //				 */
    //				body_04.setAccountNumber(config_04); // 这个科目
    //				body_04.setOriginalAmount(rent); // 原币金额
    //				body_04.setEntryDC(CR); // 借贷方向 (DR(1) 借方- CR(1) 贷方)
    //				body_04.setVoucherAbstract(voucherAbstractrent); // 摘要
    //				body_04.setCurrencyNumber(currencyNumber); // 币别数据字段对象
    //				body_04.setIntereasVoucherasStactsInAction(stactsList_04); // 保存辅助账
    //
    //				/** 建立凭证体1-结束 */
    //
    //				bodyList.add(body_04);
    //			}
    //		}

    /**
     * ************************************ 凭证体2-分割线-结束
     * *******************************************************
     */

    /** 第一步:建立凭证头 */
    /**
     * ************************************ 凭证头-分割线-开始
     * *******************************************************
     */

    /**
     * 凭证头构建-开始 参数1:companyNumber 所属公司 取:对应UUID 或 InterOrgCode 中的 orgCode 组织机构代码 注:德银要求不做
     * 参数2:bizDate 业务发生日期 取:如果是网银,则取网银到账日期,其他留空.待导出本系统时取当时日期, 例: 收款流程 中取网银到账日期 参数3:bookedDate 财务记账日期
     * 取:同业务发生日期 参数4:voucherType 凭证字 取: 汉字 "记账凭证" 参数5:generateDate 凭证发生日期 取: 系统当前时间 参数6:modleName
     * 业务模块 取:标准当前流程名称(凭证产生的模块流程名称) 例:[收款流程] 参数7:contract_number 业务合同号 取:当前实例业务合同号(存在则比填,不存在则传空)
     * 例:[德银2013第1号] 参数8: creator 制单人 取:当前业务人员 参数9:memo1 备注 取:存在则填,不存在则传空
     */
    IntereasVoucherHead headObj = new IntereasVoucherHead();

    headObj.setCompanyNumber(interOrgCode); // 参数1:所属公司组织机构代码
    headObj.setBizDate(currentDate); // 业务发生日期
    headObj.setBookedDate(currentDate); // 财务记账日期
    headObj.setVoucherType(voucherType); // 凭证字
    headObj.setGenerate_date(currentDate); // 凭证发生日期
    headObj.setModleName(workFlowName); // 当前流程名称
    headObj.setContract_id(contractInfo); // 业务合同号 【德银2013第1号】
    headObj.setCreator(currentUser); // 制单人
    headObj.setIntereasVoucherEntriesInAction(bodyList); // 保存凭证体

    // 添加到凭证头集合
    headObjList.add(headObj);
    /** 凭证头构建-结束 */

    /**
     * ************************************ 凭证头-分割线-结束
     * *******************************************************
     */

    /**
     * ************************************ 分割线-以下不动
     * **********************************************************
     */

    /** 第三步:保存凭证 */
    voucherService.saveVoucherMain(headObjList);
  }
Ejemplo n.º 10
0
/** @author Adrian Cole */
public class CreateServerOptions extends BindToJsonPayload {

  static class File {
    private final String path;
    private final String contents;

    public File(String path, byte[] contents) {
      this.path = checkNotNull(path, "path");
      this.contents = Base64.encodeBytes(checkNotNull(contents, "contents"));
      checkArgument(
          path.getBytes().length < 255,
          String.format(
              "maximum length of path is 255 bytes.  Path specified %s is %d bytes",
              path, path.getBytes().length));
      checkArgument(
          contents.length < 10 * 1024,
          String.format(
              "maximum size of the file is 10KB.  Contents specified is %d bytes",
              contents.length));
    }

    public String getContents() {
      return contents;
    }

    public String getPath() {
      return path;
    }
  }

  @SuppressWarnings("unused")
  private class ServerRequest {
    final String name;
    final int imageId;
    final int flavorId;
    Map<String, String> metadata;
    List<File> personality;
    Integer sharedIpGroupId;
    Addresses addresses;

    private ServerRequest(String name, int imageId, int flavorId) {
      this.name = name;
      this.imageId = imageId;
      this.flavorId = flavorId;
    }
  }

  private Map<String, String> metadata = Maps.newHashMap();
  private List<File> files = Lists.newArrayList();
  private Integer sharedIpGroupId;
  private String publicIp;

  @Override
  public void bindToRequest(HttpRequest request, Map<String, String> postParams) {
    ServerRequest server =
        new ServerRequest(
            checkNotNull(postParams.get("name"), "name parameter not present"),
            Integer.parseInt(
                checkNotNull(postParams.get("imageId"), "imageId parameter not present")),
            Integer.parseInt(
                checkNotNull(postParams.get("flavorId"), "flavorId parameter not present")));
    if (metadata.size() > 0) server.metadata = metadata;
    if (files.size() > 0) server.personality = files;
    if (sharedIpGroupId != null) server.sharedIpGroupId = this.sharedIpGroupId;
    if (publicIp != null) {
      server.addresses = new Addresses();
      server.addresses.getPublicAddresses().add(publicIp);
      server.addresses.setPrivateAddresses(null);
    }
    bindToRequest(request, ImmutableMap.of("server", server));
  }

  /**
   * You may further customize a cloud server by injecting data into the file system of the cloud
   * server itself. This is useful, for example, for inserting ssh keys, setting configuration
   * files, or storing data that you want to retrieve from within the instance itself. It is
   * intended to provide a minimal amount of launch-time personalization. If significant
   * customization is required, a custom image should be created. The max size of the file path data
   * is 255 bytes while the max size of the file contents is 10KB. Note that the file contents
   * should be encoded as a Base64 string and the 10KB limit refers to the number of bytes in the
   * decoded data not the number of characters in the encoded data. The maximum number of file
   * path/content pairs that can be supplied is 5. Any existing files that match the specified file
   * will be renamed to include the extension bak followed by a time stamp. For example, the file
   * /etc/passwd will be backed up as /etc/passwd.bak.1246036261.5785. All files will have root and
   * the root group as owner and group owner, respectively and will allow user and group read access
   * only (-r--r-----).
   */
  public CreateServerOptions withFile(String path, byte[] contents) {
    checkState(files.size() < 5, "maximum number of files allowed is 5");
    files.add(new File(path, contents));
    return this;
  }

  /**
   * A shared IP group is a collection of servers that can share IPs with other members of the
   * group. Any server in a group can share one or more public IPs with any other server in the
   * group. With the exception of the first server in a shared IP group, servers must be launched
   * into shared IP groups. A server may only be a member of one shared IP group.
   *
   * <p>Servers in the same shared IP group can share public IPs for various high availability and
   * load balancing configurations. To launch an HA server, include the optional sharedIpGroupId
   * element and the server will be launched into that shared IP group.
   *
   * <p>Note: sharedIpGroupId is an optional parameter and for optimal performance, should ONLY be
   * specified when intending to share IPs between servers.
   *
   * @see #withSharedIp(String)
   */
  public CreateServerOptions withSharedIpGroup(int id) {
    checkArgument(id > 0, "id must be positive or zero.  was: " + id);
    this.sharedIpGroupId = id;
    return this;
  }

  /**
   * Custom cloud server metadata can also be supplied at launch time. This metadata is stored in
   * the API system where it is retrievable by querying the API for server status. The maximum size
   * of the metadata key and value is each 255 bytes and the maximum number of key-value pairs that
   * can be supplied per server is 5.
   */
  public CreateServerOptions withMetadata(Map<String, String> metadata) {
    checkNotNull(metadata, "metadata");
    checkArgument(
        metadata.size() <= 5,
        "you cannot have more then 5 metadata values.  You specified: " + metadata.size());
    for (Entry<String, String> entry : metadata.entrySet()) {
      checkArgument(
          entry.getKey().getBytes().length < 255,
          String.format(
              "maximum length of metadata key is 255 bytes.  Key specified %s is %d bytes",
              entry.getKey(), entry.getKey().getBytes().length));
      checkArgument(
          entry.getKey().getBytes().length < 255,
          String.format(
              "maximum length of metadata value is 255 bytes.  Value specified for %s (%s) is %d bytes",
              entry.getKey(), entry.getValue(), entry.getValue().getBytes().length));
    }
    this.metadata = metadata;
    return this;
  }

  /**
   * Public IP addresses can be shared across multiple servers for use in various high availability
   * scenarios. When an IP address is shared to another server, the cloud network restrictions are
   * modified to allow each server to listen to and respond on that IP address (you may optionally
   * specify that the target server network configuration be modified). Shared IP addresses can be
   * used with many standard heartbeat facilities (e.g. keepalived) that monitor for failure and
   * manage IP failover.
   *
   * <p>If you intend to use a shared IP on the server being created and have no need for a separate
   * public IP address, you may launch the server into a shared IP group and specify an IP address
   * from that shared IP group to be used as its public IP. You can accomplish this by specifying
   * the public shared IP address in your request. This is optional and is only valid if
   * sharedIpGroupId is also supplied.
   */
  public CreateServerOptions withSharedIp(String publicIp) {
    checkState(
        sharedIpGroupId != null, "sharedIp is invalid unless a shared ip group is specified.");
    this.publicIp = checkNotNull(publicIp, "ip");
    return this;
  }

  public static class Builder {

    /** @see CreateServerOptions#withFile(String,byte []) */
    public static CreateServerOptions withFile(String path, byte[] contents) {
      CreateServerOptions options = new CreateServerOptions();
      return options.withFile(path, contents);
    }

    /** @see CreateServerOptions#withSharedIpGroup(int) */
    public static CreateServerOptions withSharedIpGroup(int id) {
      CreateServerOptions options = new CreateServerOptions();
      return options.withSharedIpGroup(id);
    }

    /** @see CreateServerOptions#withMetadata(Map<String, String>) */
    public static CreateServerOptions withMetadata(Map<String, String> metadata) {
      CreateServerOptions options = new CreateServerOptions();
      return options.withMetadata(metadata);
    }

    /** @see CreateServerOptions#withSharedIp(String) */
    public static CreateServerOptions withSharedIp(String publicIp) {
      CreateServerOptions options = new CreateServerOptions();
      return options.withSharedIp(publicIp);
    }
  }
}