/** * 一覧共通component用、CreateRelationListPagingCtrlBase と一緒に使う * https://developer.salesforce.com/page/Controller_Component_Communication */ public without sharing class CreateRelationListPagingCmpCtrl { public CreateRelationListPagingCtrlBase pageController { get; set { System.debug('SSSSSSSSSSSSSSSSS: setPageController start'); if (value!= null) { pageController = value; pageController.setComponentController(this); } System.debug('SSSSSSSSSSSSSSSSS: setPageController end'); } } // 一覧ページにチェックボックスがあるか (compnent の param) public Integer bottomSp {get; set;} public Boolean hasChkBox { get; set; } public Boolean noSortHeader { get; set; } public Boolean isRadioBox { get; set; } public String tabS {get; set;} public List allrecords{get;set;} public transient Boolean userCustomPaging {get; set;} public Boolean isNeedCheckEvent { get { return pageController.isNeedCheckEvent; } set; } // ページングを表示かどうか public Boolean isDisplayPaging { get; set; } public Integer page { get; set; } // current page public Integer maxPage { get; set; } // 総件数 public Integer recordAllCount { get; set; } // private // 当然ページに件数 public Integer currentPageRecordCnt { get; set; } // private public Boolean hasPrevious { get; set; } public Boolean hasNext { get; set; } public Boolean canGoPage { get; set; } // getSearchNumMax >2000 の場合 goPageInt を非表示すること public List pageNumList { get { pageNumList = new List(); for (Integer pi = 1; pi < maxPage+1; pi++) { pageNumList.add(new SelectOption(String.valueOf(pi), String.valueOf(pi))); } return pageNumList; } private set; } // TODO ほしいのは、 << < ... 5,6,7 ... > >> がベストすが、一応そのまま public Integer goPageInt { get; set; } // TODO wwf getSearchNumMax >2000 の場合 goPageInt を非表示すること // OFFSET句で使用可能な最大値 public static final Integer OFFSET_LIMIT = 2000; // ページレイアウトの情報を取得 private Map DESC_RW = null; // FIXME where のcacheは? CmpCtrlの属性のはず /*****************ソートキー******************/ public Object sort1stValue { get; set; } public Object sortLastValue { get; set; } public Object sort1stId { get; set; } public Object sortLastId { get; set; } public String sortKey { get; set; } public String preSortKey { get; set; } public Boolean sortOrderAsc { get; set; } public String[] sortOrder { get; set; } // TODO sortOrderLeft と sortOrderRight public String[] columus = new String[]{}; public Set columusSet = new Set(); // 项目set 字段标签 public List titleLeft { get; private set; } public List titleRight { get; private set; } // 项目set 字段名 public List> columnsLeftApi { get; private set; } // 参照項目用 public List> columnsRightApi { get; private set; } // 参照項目用 public List columnsHiddenApi { get; private set; } // 一階層のみ public List columnLeftCss { get; private set; } // css 用(もとはapi名) public List columnRightCss { get; private set; } // css 用(もとはapi名) public Map columnLeftRW { get; private set; } // r,w,wm用 public Map columnRightRW { get; private set; } // r,w,wm用 public String strColumus { get; private set; } // Soqlに検索対応項目 public Boolean isFirstRefresh { get; private set;} // 1回目かどうか public Schema.Sobjecttype sObjType; //need add remote site flg public Boolean MetadataConnectionWarning {get;set;} public CreateRelationListPagingCmpCtrl() { System.debug('SSSSSSSSSSSSSSSSS: CreateRelationListPagingCmpCtrl start'); isFirstRefresh = true; canGoPage = true; page = 1; maxPage = 1; recordAllCount = 0; System.debug('SSSSSSSSSSSSSSSSS: CreateRelationListPagingCmpCtrl end'); } public void init() { System.debug('SSSSSSSSSSSSSSSSS: CreateRelationListPagingCmpCtrl.init start'); MetadataConnectionWarning = false; if(String.isBlank(pageController.parentId)){ pageController.isNeedSearchFirst = true; isFirstRefresh = false; ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR, 'パラメータ:Id必须指定')); return; } if(String.isBlank(pageController.getRecordTypeId() )){ try { System.debug('--------2--------'+pageController.getObjName()); Map> editLayoutItemRWMap = SoapApi.getEditLayoutItemRW(pageController.getObjName(), null); System.debug('--------1--------'+editLayoutItemRWMap); if(editLayoutItemRWMap.size() >0){ DESC_RW = editLayoutItemRWMap.values()[0]; } } catch (Exception e) { if (e.getMessage().contains('Remote site settings')) { MetadataConnectionWarning = true; return ; } else { throw e; } } }else{ try { DESC_RW = SoapApi.getEditLayoutItemRW(pageController.getObjName(), new String[]{pageController.getRecordTypeId()}).get(pageController.getRecordTypeId()); } catch (Exception e) { if (e.getMessage().contains('Remote site settings')) { MetadataConnectionWarning = true; return ; } else { throw e; } } } if(String.isNotBlank(pageController.getObjName())){ sObjType = (Schema.Sobjecttype) Schema.getGlobalDescribe().get(pageController.getObjName()); }else{ // TODO error } //FieldSetより画面中出す項目を設定する setLayoutRWInfo(); getSelectedDataInfo(); //検索結果を取得する searchAndPaging(); if (String.isNotBlank(String.valueOf(pageController.getSearchNumMax())) && pageController.getSearchNumMax() > OFFSET_LIMIT) { canGoPage = false; //goPage表示しない } System.debug('SSSSSSSSSSSSSSSSS: CreateRelationListPagingCmpCtrl.init end'); return ; } /* FieldSetより画面中出す項目を設定する*/ // FIXME 一回だけ呼べばいいのはず、途中fieldset変更した場合、画面を閉じれるOKじゃない? private void setLayoutRWInfo() { system.debug('●●●●● objName ' + pageController.getObjName()); system.debug('●●●●● getSqlWhereStr() ' + pageController.sqlWhereStr); system.debug('●●●●● getColumnLeftFieldSetName() ' + pageController.getColumnLeftFieldSetName()); system.debug('●●●●● getColumnRightFieldSetName() ' + pageController.getColumnRightFieldSetName()); if (this.sortOrder == null) { try { // 获得项目set // FIXME エラー catch Map fsMap = Schema.getGlobalDescribe().get(pageController.getObjName()).getDescribe().fieldSets.getMap(); // 左 固定 Schema.FieldSet fs = fsMap.get(pageController.getColumnLeftFieldSetName()); system.debug('●●●●● fs ' + fs); // 获得项目set中的所有项目 List fsmList = new List(); if(fs!=null){ fsmList = fs.getFields(); } // 获得字段标签和字段名 titleLeft = new List(); List columnLeft = new List(); columnLeftCss = new List(); columnsLeftApi = new List>(); columnLeftRW = new Map(); for (FieldSetMember fsm : fsmList) { titleLeft.add(fsm.getLabel()); columnLeft.add(fsm.getFieldPath()); List splitFieldPath = fsm.getFieldPath().split('\\.'); columnsLeftApi.add(splitFieldPath); if (DESC_RW == null) { columnLeftRW.put(fsm.getFieldPath(), 'r'); } else if (splitFieldPath.size() == 1) { String rw = DESC_RW.get(fsm.getFieldPath()); if (rw != null) { columnLeftRW.put(fsm.getFieldPath(), rw); } else { columnLeftRW.put(fsm.getFieldPath(), 'r'); } } else { columnLeftRW.put(fsm.getFieldPath(), 'r'); } } for (String s : columnLeft) { if (columusSet.contains(s) == false) { columus.add(s); columusSet.add(s); } columnLeftCss.add(s.replace('.','_')); } fs = fsMap.get(pageController.getColumnRightFieldSetName()); // 获得项目set中的所有项目 fsmList = fs.getFields(); // 获得字段标签和字段名 titleRight = new List(); List columnRight = new List(); columnRightCss = new List(); columnsRightApi = new List>(); columnRightRW = new Map(); for (FieldSetMember fsm : fsmList) { titleRight.add(fsm.getLabel()); columnRight.add(fsm.getFieldPath()); List splitFieldPath = fsm.getFieldPath().split('\\.'); columnsRightApi.add(splitFieldPath); if (DESC_RW == null) { columnRightRW.put(fsm.getFieldPath(), 'r'); } else if (splitFieldPath.size() == 1) { String rw = DESC_RW.get(fsm.getFieldPath()); if (rw != null) { columnRightRW.put(fsm.getFieldPath(), rw); } else { columnRightRW.put(fsm.getFieldPath(), 'r'); } } else { columnRightRW.put(fsm.getFieldPath(), 'r'); } } for (String wField : pageController.getWritableColumnFieldList()) { system.debug('●●●●● wField:' + columnRightRW.get(wField)); if (columnRightRW.containsKey(wField) && columnRightRW.get(wField) == 'r') { columnRightRW.put(wField, 'w'); } } for (String s : columnRight) { if (columusSet.contains(s) == false) { columus.add(s); columusSet.add(s); } columnRightCss.add(s.replace('.','_')); } strColumus = String.join(columus, ','); // 获得 additionalInfoMap 字段 columnsHiddenApi = new List(); for (String colname : pageController.getHiddenFieldList()) { if (!columusSet.contains(colname)) { strColumus += ',' + colname; } columnsHiddenApi.add(colname); } // strColumus 里加 field for (String colname : pageController.getColumnFieldList()) { if (!columusSet.contains(colname)) { strColumus += ',' + colname; } } if (!String.isBlank(pageController.getFKColumnField()) && !columusSet.contains(pageController.getFKColumnField())) { strColumus += ',' + pageController.getFKColumnField(); } if (String.isNotBlank(pageController.getWidth0Field())) { String firstOrder = pageController.getWidth0Field(); if (!columus.contains(firstOrder)) { columus.add(firstOrder); this.sortKey = String.valueOf(columus.size() - 1); } else { for (Integer i = 0; i < columus.size(); i++) { if (columus[i] == firstOrder) { this.sortKey = String.valueOf(i); break; } } } } // FIXME2 ここからしたのロジックなにか意味ありますか? このメソッドは更新権限とクエリ項目作成用 this.sortOrderAsc = true; this.sortOrder = new String[columus.size()]; // TODO sortOrderLeft と sortOrderRight for (Integer i = 0; i < columus.size(); i++) this.sortOrder[i] = ' '; system.debug('●●●●● sortOrder ' + sortOrder.size()); } catch (Exception e) { ApexPages.addmessage(new ApexPages.message(ApexPages.severity.Error, e.getMessage())); } } } public void getSelectedDataInfo() { if (!String.isBlank(pageController.getSelectedDataSql())) { String soql =''; final String soqlStr = 'Select {0} {1} '; soql += String.format(soqlStr, new String[] {strColumus , pageController.getSelectedDataSql()}); // system.debug(pageController + 'getSelectedDataInfo soql ' + soql); system.debug('zheli~getSelectedDataInfo soql ' + soql); List queryList = Database.query(soql); pageController.selectedData = queryList; pageController.makeSelectedDataInfo(); } } /* コンポーネントにSoqlを発行して、ページングする*/ /* Search対象:original sObjType */ public void searchAndPaging() { try { System.debug('searchAndPaging start!!!'); system.debug('●●●●● sortKey ' + this.sortKey); if (!pageController.getIsNeedRunSearch() || !pageController.isNeedSearchFirst) { isFirstRefresh = false; pageController.isNeedSearchFirst = true; // 一回目falseの時、ここで trueになる // get selected data pageController.setViewList(new List()); currentPageRecordCnt = pageController.viewList.size(); allrecords = pageController.viewList; System.debug(LoggingLevel.INFO, '*** allrecords: ' + allrecords); recordAllCount = currentPageRecordCnt; pageController.sqlWhereStr = ''; return; } isFirstRefresh = false; pageController.isNeedSearchFirst = true; if (pageController.sqlWhereStr == '') return; String whereStr = pageController.sqlWhereStr; if (!String.isBlank(pageController.getFKColumnField()) && pageController.selectedFKIdList != null && pageController.selectedFKIdList.size() > 0) { String collegeIdString = '\''; collegeIdString += String.join(pageController.selectedFKIdList, '\',\''); collegeIdString += '\''; //这里会把检索出来数据的ID从soql里面除掉 whereStr += ' and Id not in (' + collegeIdString + ') '; } Integer offsetLimit = OFFSET_LIMIT; if (String.isNotBlank(String.valueOf(pageController.getSearchNumMax()))) { offsetLimit = Math.max(pageController.getSearchNumMax(), OFFSET_LIMIT); } Integer offsetLimitAddOne = offsetLimit + 1; String soql = this.makeSoql('count()', pageController.getOriginObjName(), whereStr, '', ' LIMIT :offsetLimitAddOne' ); // FIXME count() と select 2つあるので、transaction startしてください。 recordAllCount = Database.countQuery(soql); if (pageController.getShowRecordCountMSG()) { /* ページング*/ if (recordAllCount > offsetLimit) { final String err2Str = '数据超过 {0} 件,只显示前 {1} 件'; String err2 = String.format(err2Str, new String[] {offsetLimit+'', offsetLimit+''}); ApexPages.addmessage(new ApexPages.message(ApexPages.severity.WARNING, err2)); recordAllCount--; } else { final String err3Str = '取得了 {0} 条数据'; String err3 = String.format(err3Str, new String[] {recordAllCount+''}); ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO, err3)); } } Integer pageSize = 0; // 画面のパラメータ:件数/ページ // FIXME2 ページングいらないページを計算する必要もないー>すべてのCountを取得しなくでもいいでは?→ページ数の表示方を検討要 if(String.isBlank(pageController.pageSize)){ pageSize = OFFSET_LIMIT; if (String.isNotBlank(String.valueOf(pageController.getSearchNumMax()))) { pageSize = Math.min(pageController.getSearchNumMax(), OFFSET_LIMIT); } isDisplayPaging = false; }else{ pageSize = Integer.valueOf(pageController.pageSize); isDisplayPaging = true; } // 指定可能な最大ページ番号 Integer offsetMaxPage = (offsetLimit + pageSize - 1) / pageSize + 1; maxPage = (recordAllCount - 1) / pageSize + 1; // 表示ページの調整 page = Math.min(Math.min(page, maxPage), offsetMaxPage); Integer offset = (page - 1) * pageSize; hasPrevious = offset > 0; hasNext = offset + pageSize < Math.min(recordAllCount, offsetLimit + 1); String limitClause = ' LIMIT :pageSize OFFSET :offset'; // TODO wwf please check if (String.isNotBlank(String.valueOf(pageController.getSearchNumMax())) && pageController.getSearchNumMax() > OFFSET_LIMIT && String.isNotBlank(this.sortKey)) { // and sort condition Object o_1stValue = this.sort1stValue; Object o_LastValue = this.sortLastValue; Object o_1stId = this.sort1stId; Object o_LastId = this.sortLastId; if (goPageInt == null && page == 1) { // sort condition 追加なし } // next page else if (page > goPageInt || (goPageInt == null && page > 1)) { limitClause = ' LIMIT :pageSize'; if (this.sortOrderAsc == true) { // asc next if (o_LastValue != null) { whereStr += ' and (' + this.columus[Integer.valueOf(this.sortKey)] + ' > :o_LastValue ' + 'or (' + this.columus[Integer.valueOf(this.sortKey)] + ' = :o_LastValue and Id > :o_LastId)' + ')'; } else { whereStr += ' and (' + this.columus[Integer.valueOf(this.sortKey)] + ' <> null ' + 'or (' + this.columus[Integer.valueOf(this.sortKey)] + ' = null and Id > :o_LastId)' + ')'; } } else { // desc next if (o_LastValue != null) { whereStr += ' and (' + this.columus[Integer.valueOf(this.sortKey)] + ' < :o_LastValue ' + 'or (' + this.columus[Integer.valueOf(this.sortKey)] + ' = :o_LastValue and Id < :o_LastId)' + ')'; } else { whereStr += ' and (' + this.columus[Integer.valueOf(this.sortKey)] + ' = null ' + ' and Id < :o_LastId' + ')'; } } } // prev page else if (page < goPageInt) { limitClause = ' LIMIT :pageSize'; if (this.sortOrderAsc == true) { // asc prev if (o_1stValue != null) { whereStr += ' and (' + this.columus[Integer.valueOf(this.sortKey)] + ' < :o_1stValue ' + 'or (' + this.columus[Integer.valueOf(this.sortKey)] + ' = :o_1stValue and Id < :o_1stId)' + ')'; } else { whereStr += ' and (' + this.columus[Integer.valueOf(this.sortKey)] + ' = :o_1stValue ' + ' and Id < :o_1stId' + ')'; } } else { // desc prev if (o_1stValue != null) { whereStr += ' and (' + this.columus[Integer.valueOf(this.sortKey)] + ' > :o_1stValue ' + 'or (' + this.columus[Integer.valueOf(this.sortKey)] + ' = :o_1stValue and Id > :o_1stId)' + ')'; } else { whereStr += ' and (' + this.columus[Integer.valueOf(this.sortKey)] + ' <> null ' + 'or (' + this.columus[Integer.valueOf(this.sortKey)] + ' = null and Id > :o_1stId)' + ')'; } } } // go page else { // sort condition 追加なし } } String orderStr = ' '; if (String.isBlank(this.sortKey)) { String ctrlOrderStr = pageController.getOrderbyStr(); if (String.isBlank(ctrlOrderStr)) { orderStr += 'order by LastModifiedDate desc '; } else { orderStr += ctrlOrderStr + ' '; } } else { orderStr += 'order by ' + this.columus[Integer.valueOf(this.sortKey)] + ' ' + (this.sortOrderAsc == true ? 'asc nulls first, Id asc' : 'desc nulls last, Id desc') ; } System.debug('orderStr' + orderStr); System.debug('whereStr' + whereStr); // ここ getOriginObjColumns して、setViewList() のなか、再度 strColumus より探します。 if (String.isNotBlank(this.sortKey)) { String sortCol = this.columus[Integer.valueOf(this.sortKey)]; String columsWithSortKey = pageController.getOriginObjColumns(); if (!columsWithSortKey.contains(sortCol)) { columsWithSortKey += ', ' + sortCol; } soql = this.makeSoql(columsWithSortKey, pageController.getOriginObjName(), whereStr, orderStr, limitClause ); } else { soql = this.makeSoql(pageController.getOriginObjColumns(), pageController.getOriginObjName(), whereStr, orderStr, limitClause ); } system.debug('●●●●● soql ' + soql); List queryList = Database.query(soql); // sort1stValue と sortLastValue if (queryList.size() > 0 && String.isNotBlank(this.sortKey)) { List sortKeyCols = this.columus[Integer.valueOf(this.sortKey)].split('\\.'); sort1stValue = CreateRelationListPagingCmpCtrl.getObjectValue(queryList[0], sortKeyCols); sort1stId = queryList[0].Id; sortLastValue = CreateRelationListPagingCmpCtrl.getObjectValue(queryList[queryList.size() - 1], sortKeyCols); sortLastId = queryList[queryList.size() - 1].Id; } pageController.setViewList(queryList); currentPageRecordCnt = pageController.viewList.size(); allrecords = pageController.viewList; System.debug(LoggingLevel.INFO, '*** allrecords: ' + allrecords); system.debug('●●●●● searchAndPaging END ' ); } catch (Exception e) { System.debug(e.getStackTraceString()); ApexPages.addmessage(new ApexPages.message(ApexPages.severity.Error,e.getMessage())); } } public void sortTable() { if(String.isNotBlank(pageController.sortKey)){ this.sortKey = pageController.sortKey; } String sort_param = ApexPages.currentPage().getParameters().get('sortKey'); if (String.isNotBlank(sort_param)) { this.sortKey = sort_param; } if (this.sortKey == this.preSortKey) { if (String.isBlank(this.sortKey) == false) { // 方向が変わるのみ this.sortOrderAsc = !this.sortOrderAsc; this.sortOrder[Integer.valueOf(this.sortKey)] = (this.sortOrderAsc == true ? '↑' : '↓'); } } else { this.sortOrderAsc = true; if (String.isBlank(this.preSortKey) == false) { this.sortOrder[Integer.valueOf(this.preSortKey)] = ' '; } this.sortOrder[Integer.valueOf(this.sortKey)] = (this.sortOrderAsc == true ? '↑' : '↓'); } this.preSortKey = this.sortKey; //FIXME2 200行目による this.sortOrderAsc = true; false になることはない //setLayoutRWInfo(); //getSelectedDataInfo(); page = 1; goPageInt = null; searchAndPaging(); goPageInt = page; return; } public static Object getObjectValue(sObject obj, List cols) { if (obj == null) return null; if (cols.size() == 1) { return obj.get(cols[0]); } else { sObject obj_r = obj.getSObject(cols[0]); cols.remove(0); return getObjectValue(obj_r, cols); } } /* Soql文を作成する*/ private String makeSoql(String sltStr, String fromObj, String whereStr, String orderStr, String limitStr) { String soql =''; final String soqlStr = 'Select {0} From {1} {2} {3} {4} '; soql += String.format(soqlStr, new String[] {sltStr , fromObj , whereStr , orderStr , limitStr}); system.debug('makeSoql'+soql); return soql; } public void searchGoPage() { try { page = goPageInt; //setLayoutRWInfo(); //getSelectedDataInfo(); searchAndPaging(); } catch (Exception e) { ApexPages.addMessages(e); } return; } public void searchNext() { try { page = page + 1; //setLayoutRWInfo(); //getSelectedDataInfo(); searchAndPaging(); goPageInt = page; } catch (Exception e) { ApexPages.addMessages(e); } return; } public void searchPrevious() { try { page = page - 1; //setLayoutRWInfo(); //getSelectedDataInfo(); searchAndPaging(); goPageInt = page; } catch (Exception e) { ApexPages.addMessages(e); } return; } //// assignTo="{!clickLineNo}" より url を組み立てる //// TODO rerenderなど //public void checkEvent() { // pageController.checkEvent(clickLineNo); //} @testVisible private void testI() { Integer i = 0; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; i++; } }