變更開始

DATA-GEN 產生器的範例

附註: 僅針對與 DATA-GEN 作業相關的範例各方面提供詳細說明。

在此範例中,產生器會為 DATA-GEN 作業產生 HTML 表格。

如果您想要嘗試執行範例中的程式碼,請參閱 SQL 陳述式以建立範例使用的檔案 ,以取得 SQL 陳述式以建立程式使用的 file

如需程式所產生的 HTML ,請參閱 範例中 DATA-GEN 作業所產生的輸出

具有 DATA-GEN 作業的 RPG 程式

下列顯示使用 DATA-GEN 作業的 RPG 程式。

請注意程式的下列各方面:
  1. 資料結構以 HTML 表格中預期的每一列的子欄位來定義。

    外部說明資料結構使用 EXTFLD 陳述式來設定 HTML 表格中直欄標題所需的名稱大小寫。 對於 ITEMPRICE 欄位, EXTFLD 陳述式也會新增底線。 產生直欄標題時,產生器會將底線取代為空白。

  2. 對於前三個 DATA-GEN 作業,輸出檔指定在 %DATA 內建函數的第一個運算元中。 選項 "doc = file" 指出第一個運算元是檔案的名稱。
  3. 執行產生的程式指定為 %GEN 內建函數的第一個運算元。 請參閱 程式以產生 HTML 表格 ,以取得程式的原始檔。 執行產生作業的程式支援選用字元或 UCS-2 值作為 %GEN 內建函數的第二個運算元。 此值用作表格的標題。
  4. DATA-GEN *START 作業會啟動 DATA-GEN 序列。
  5. 下一個 DATA-GEN 作業會繼續該順序。 它會在 HTML 表格中寫出一列。
  6. DATA-GEN *END 作業結束順序。
  7. 最終 DATA-GEN 不是序列的一部分。 DATA-GEN 作業的結果會放入指定為 %DATA 內建函數的第一個運算元的變數中。

**free

DCL-C FILENAME 'MYORDERS';
DCL-F orders EXTDESC(FILENAME)
             EXTFILE(*EXTDESC);
DCL-DS order EXTNAME(FILENAME : *INPUT)             //  1 
             QUALIFIED;
   Name extfld('NAME');
   Type extfld('ITEMTYPE');
   Item_Price extfld('ITEMPRICE');
END-DS;
DCL-DS customer QUALIFIED;                          //  1 
   Name VARCHAR(30);
   Address VARCHAR(100);
   Zip_Code PACKED(9);
END-DS;

DCL-S customerTable VARCHAR(1000);

DATA-GEN *START %DATA('order.html' : 'doc=file')    //  2, 4 
                %GEN('GENHTMLTAB'
                   : 'Order for ' + %CHAR(%DATE()));//  3 

READ orders order;
DOW NOT %EOF;
   DATA-GEN order %DATA('order.html'
                      : 'doc=file output=continue') //  2, 5 
                  %GEN('GENHTMLTAB');               //  3 
   READ orders order;
ENDDO;

DATA-GEN *END %DATA('order.html' : 'doc=file')      //  2, 6 
              %GEN('GENHTMLTAB');                   //  3 

customer.Name = 'A. Smith';
customer.Address = '123 Elm Street';
customer.Zip_Code = 11111;
DATA-GEN customer %DATA(customerTable)              //  7 
                  %GEN('GENHTMLTAB');               //  3 

*INLR = '1';

產生 HTML 表格的程式

請注意模組起始區段的下列層面:

  1. 這個發電機是一個程式。
  2. 由於傳遞至產生器之參數的 名稱 子欄位具有 UTF-16 資料,因此可方便將 UCS-2 項目的預設 CCSID 設為 UTF-16。
  3. 檔案 QOAR/QRPGLESRC 中的副本成員 QRNDTAGEN 定義傳遞給產生器的參數,以及產生器所需的其他資訊的具名常數。
  4. state_t 資料結構定義產生器用來追蹤產生作業的資訊。
  5. 針對此產生器偵測到的錯誤,定義了數個錯誤碼及相符訊息。

**free

CTL-OPT OPTION(*SRCSTMT);
CTL-OPT MAIN(genHtmlTab);            //  1 
CTL-OPT CCSID(*UCS2 : *UTF16);       //  2 
/IF DEFINED(*CRTBNDRPG)
   CTL-OPT DFTACTGRP(*NO);
/ENDIF

/copy QOAR/QRPGLESRC,QRNDTAGEN       //  3 

DCL-C MAX_CAPTION 10000;

DCL-DS state_t qualified template;   //  4 
   haveHeader IND;
   inDataStructure IND;
   dataStructureName LIKE(QrnDgName_t);
   numSubfields INT(10);
   caption VARUCS2(MAX_CAPTION);
   haveCaption IND;
END-DS;

DCL-DS errorCodes qualified;         //  5 
   DCL-DS nestedStructNotAllowed;
      code INT(10) INZ(1);
      msg VARCHAR(100) INZ('Nested structures are not allowed.');
   END-DS;
   DCL-DS differentStruct;
      code INT(10) INZ(2);
      msg VARCHAR(100)
          INZ('The data structure is not the same.');
   END-DS;
   DCL-DS eventNotSupported;
      code INT(10) INZ(3);
      msg VARCHAR(100) INZ('The event is not supported.');
   END-DS;
   DCL-DS valueNotInStruct;
      code INT(10) INZ(4);
      msg VARCHAR(100) INZ('All values must be subfields.');
   END-DS;
   DCL-DS userParmTypeNotSupported;
      code INT(10) INZ(4);
      msg VARCHAR(100) INZ('The user-parm must be UTF-16 or alphanumeric with the job CCSID.');
   END-DS;
END-DS;

主程序

請注意程序的下列層面:

  1. 單一參數會傳遞至產生器。
  2. 狀態 資料結構會針對 DATA-GEN 作業或 DATA-GEN 作業序列對產生器的所有呼叫,保留產生器所維護的狀態資訊。

    資料結構是根據指標,因為資料結構的儲存體將從資料堆配置。

  3. 從傳給產生器之參數的 env 子欄位中設定指標 pEnv ,可讓產生器呼叫回呼程序
    警告: QrnDgEvent_12_Terminate 事件的 env 指標是空值。

    在此事件期間,請勿嘗試呼叫任何回呼程序。

  4. 當產生完成時,產生器需要取消配置狀態指標,因此它會啟用 QrnDgEvent_12_Terminate 事件。
  5. QrnDgEvent_12_Terminate 事件期間不容許回呼,因此產生器只會取消配置狀態指標並傳回。
  6. 如果尚未配置狀態資訊,產生器會在傳遞給產生器的參數中配置並起始設定 generatorParm 指標。
  7. 產生器會設定 generatorParm 指標中狀態資訊的基本指標。
  8. 此事件指出一連串 DATA-GEN 作業的開始。

    如果指定 %GEN 的第二個運算元,則會在傳遞至產生器之參數的 userParm 子欄位中傳遞該運算元。 產生器將呼叫 getCaption 程序,以取得 userParm的值。

  9. 此事件指出一連串 DATA-GEN 作業的結束。 產生器會產生 HTML 表格的結尾。
  10. 此事件指出與變數相關之 DATA-GEN 作業的事件開始。

    如果指定 %GEN 的第二個運算元,則會在傳遞至產生器之參數的 userParm 子欄位中傳遞該運算元。 如果先前對產生器的呼叫尚未決定標題,則產生器會呼叫 getCaption 程序,以取得 userParm的值。

  11. 此事件指出與變數相關之 DATA-GEN 作業的事件結束。 如果 DATA-GEN 作業不是 DATA-GEN 作業序列的一部分,則產生器會產生 HTML 表格結尾。
  12. 這些事件指出資料結構陣列或單一資料結構的開始。 由於這可能是產生器第一次看到與資料結構相關的任何項目,必要的話,它會呼叫程序 genStart表格 來產生 HTML 表格的標頭。

    如果 genStart表格 程序在對產生器的呼叫中偵測到錯誤,它會將錯誤報告給 DATA-GEN ,且控制權不會回到對 genStart表格 程序的呼叫之後的陳述式。

  13. 此事件指出資料結構陣列的結尾。 此產生器在此事件期間不需要執行任何動作。
  14. 產生器會在 HTML 表格中產生列的開頭。
  15. 此事件指出資料結構的結尾。 這個產生器會在 HTML 表格中產生列的結尾。
  16. 此事件指示純量值。 如果產生器偵測到它不是子欄位,它會發出錯誤。
  17. 產生器會在 HTML 表格中產生直欄的開頭。
  18. 產生器會呼叫 QrnDgAddText 回呼程序來產生直欄的值。 傳遞給產生器之參數的 value 參數指向 UTF-16 資料,且 valueLen字元 子欄位在 UTF-16 資料中具有雙位元組字元數目。 這些參數可以直接傳遞至 QrnDgAddText 程序,因為它預期 UTF-16 資料的指標及雙位元組字元數。
  19. 產生器會在 HTML 表格中產生直欄結尾。
  20. 產生器不支援任何其他事件。

DCL-PROC genHtmlTab;
   DCL-PI *N;
      parm LIKEDS(QrnDgParm_t);                        //  1 
   END-PI;

   DCL-DS state LIKEDS(state_t) based(pState);         //  2 

   pQrnDgEnv = parm.env;                               //  3 

   parm.doTerminateEvent = *ON;                        //  4 

   IF parm.event = QrnDgEvent_12_Terminate;
      DEALLOC(N) parm.generatorState;                  //  5 
      RETURN;
   ENDIF;

   IF parm.generatorState = *NULL;                     //  6 
      parm.generatorState = %ALLOC(%SIZE(state_t));
      pState = parm.generatorState;
      CLEAR state;
   ENDIF;
   pState = parm.generatorState;                       //  7 

   IF parm.event = QrnDgEvent_01_StartMultiple;        //  8 
      IF parm.userParm <> *NULL;
         state.caption = getCaption (parm : state);
         state.haveCaption = *ON;
      ENDIF;

   ELSEIF parm.event = QrnDgEvent_02_EndMultiple;      //  9 
      genEndTable (parm : state);

   ELSEIF parm.event = QrnDgEvent_03_Start;            //  10 
      IF parm.userParm <> *NULL
      AND state.haveCaption = *OFF;
         state.caption = getCaption (parm : state);
         state.haveCaption = *ON;
      ENDIF;

   ELSEIF parm.event = QrnDgEvent_04_End;              //  11 
      IF NOT parm.isPartOfSequence;
         genEndTable (parm : state);
      ENDIF;

   ELSEIF parm.event = QrnDgEvent_09_StartStructArray; //  12 
      IF parm.userParm <> *NULL
      AND state.haveCaption = *OFF;
         state.caption = getCaption (parm : state);
         state.haveCaption = *ON;
      ENDIF;

      genStartTable (parm : state : parm.array.numSubfields);
      // Control might not return here

   ELSEIF parm.event = QrnDgEvent_10_EndStructArray;  //  13 

   ELSEIF parm.event = QrnDgEvent_05_StartStruct;      //  12 
      IF parm.userParm <> *NULL
      AND state.haveCaption = *OFF;
         state.caption = getCaption (parm : state);
         state.haveCaption = *ON;
      ENDIF;

      genStartTable (parm : state : parm.ds.numSubfields);
      // Control might not return here

      state.inDataStructure = *ON;                     //  14 
      writeLine (parm : '<tr>');

   ELSEIF parm.event = QrnDgEvent_06_EndStruct;        //  15 
      writeLine (parm : '</tr>');
      state.inDataStructure = *OFF;

   ELSEIF parm.event = QrnDgEvent_11_ScalarValue;      //  16 
      IF NOT state.inDataStructure;
         error (parm : state
              : errorCodes.valueNotInStruct.code
              : errorCodes.valueNotInStruct.msg);
         // Control will not return here
      ENDIF;

      writeLine (parm : '<td>' : *ON);           //  17 

      // The text for the column is written out in the same
      // UTF-16 CCSID as it was passed to the generator
      QrnDgAddText (parm.handle                        //  18 
                  : parm.scalar.value
                  : parm.scalar.valueLenChars);
      writeLine (parm : '</td>');                //  19 

   ELSE;
      error (parm : state                              //  20 
           : errorCodes.eventNotSupported.code
           : errorCodes.eventNotSupported.msg);
      // Control will not return here
   ENDIF;
END-PROC genHtmlTab;

getCaption 程序

此程序會從傳遞至產生器之參數的 userParm 子欄位取得表格標題的值。 此子欄位是從 DATA-GEN 作業的 %GEN 內建函數的第二個參數設定。

請注意程序的下列層面:

  1. 如果 %GEN 的第二個運算元是字串類型,則傳遞給產生器的參數具有產生器解譯 userParm 子欄位所需的所有資訊。 請參閱 傳遞至 DATA-GEN 產生器的使用者參數類型

    不過,這個產生器並不支援所有可能的類型。 如果 CCSID 不是工作 CCSID ,則它不支援英數值。 如果 CCSID 不是 UTF-16,則不支援 UCS-2 值。

    userParm 資料結構基於 parm.userParm 指標。 對於這個產生器所支援的每一種字串類型,在資料結構的位置 1 定義子欄位。

  2. %STR 內建函數會傳回以空值結尾的字串值。
  3. 如果英數值是在工作 CCSID 中,產生器會決定 parm.userParmSize 子欄位中的長度,並使用該長度來控制 %SUBST 內建函數所傳回的資料量。
  4. 如果英數值在工作 CCSID 中,則產生器不需要使用 parm.userParmSize 資訊,因為可變長度欄位在其可變長度字首中保留其自己的長度。
  5. 對於 UCS-2 或 UTF-16 值, parm.userParmSize 必須除以 2 ,以判定 %SUBST 內建函數的字元數。
  6. 如果產生器不支援使用者參數的類型或 CCSID ,則產生器會發出錯誤。

DCL-PROC getCaption;
   DCL-PI *N VARUCS2(10000) EXTPROC(*DCLCASE);
      parm LIKEDS(QrnDgParm_t);
      state LIKEDS(state_t);
   END-PI;

   DCL-DS userParm QUALIFIED BASED(P);         //  1 
      charFixed CHAR(MAX_CAPTION) POS(1);
      charVar2 VARCHAR(MAX_CAPTION) POS(1);
      charVar4 VARCHAR(MAX_CAPTION:4) POS(1);
      utf16Fixed UCS2(MAX_CAPTION) POS(1);
      utf16Var2 VARUCS2(MAX_CAPTION) POS(1);
      utf16Var4 VARUCS2(MAX_CAPTION:4) POS(1);
   END-DS;
   DCL-S CAPTION VARUCS2(MAX_CAPTION);
   DCL-S LEN INT(10);

   p = parm.userParm;
   IF parm.userParmType = QrnUserParmType_nullTerminatedString; //  2 
      caption = %STR(parm.userParm);
   ELSEIF parm.userParmType = QrnUserParmType_char              //  3 
   AND parm.userParmCcsid = QrnDg_JOB_CCSID;
      len = parm.userParmSize;
      caption = %TRIM(%SUBST(userParm.charFixed : 1 : len));
   ELSEIF parm.userParmType = QrnUserParmType_varchar_2         //  4 
   AND parm.userParmCcsid = QrnDg_JOB_CCSID;
      caption = userParm.charVar2;
   ELSEIF parm.userParmType = QrnUserParmType_varchar_4
   AND parm.userParmCcsid = QrnDg_JOB_CCSID;
      caption = userParm.charVar4;
   ELSEIF parm.userParmType = QrnUserParmType_ucs2
   AND parm.userParmCcsid = 1200;
      len = parm.userParmSize / 2;                              //  5 
      caption = %TRIM(%SUBST(userParm.utf16Fixed : 1 : len));
   ELSEIF parm.userParmType = QrnUserParmType_varucs2_2
   AND parm.userParmCcsid = 1200;
      caption = userParm.utf16var2;
   ELSEIF parm.userParmType = QrnUserParmType_varucs2_4
   AND parm.userParmCcsid = 1200;
      caption = userParm.utf16var4;
   ELSE;
      error (parm : state
           : errorCodes.userParmTypeNotSupported.code
           : errorCodes.userParmTypeNotSupported.msg);
      // Control will not return here
   ENDIF;
   RETURN caption;

END-PROC getCaption;

genStartTable 程序

此程序會使用 RPG 資料結構的子欄位名稱來產生 HTML 表格的標頭。

請注意程序的下列層面:

  1. 如果產生器已在處理資料結構,則產生器會產生錯誤狀況。
  2. 如果產生器已產生標頭,且現行資料結構的名稱或子欄位數目不同於用來產生標頭的資料結構,則產生器會產生錯誤狀況。
  3. 產生器會在其狀態資訊中設定現行資料結構的子欄位名稱及數目,因此它可以確保所遇到的資料結構全部相同。
  4. 產生器會產生 HTML 表格的開頭。
  5. 對於資料結構的每一個子欄位,產生器會在 HTML 表格的標頭中產生一列。
  6. 產生器會呼叫 QrnDgGetSubfieldName 回呼程序來取得子欄位的名稱。
  7. 產生器會產生 HTML 表格的標頭結尾。

DCL-PROC genStartTable;
   DCL-PI *n extproc(*dclcase);
      parm LIKEDS(QrnDgParm_t);
      state LIKEDS(state_t);
      numSubfields int(10) value;
   END-PI;
   DCL-S i INT(10);

   IF state.inDataStructure;                        //  1 
      error (parm : state
           : errorCodes.nestedStructNotAllowed.code
           : errorCodes.nestedStructNotAllowed.msg);
      // Control will not return here
   ENDIF;

   if state.haveHeader;
      if parm.name <> state.dataStructureName      //  2 
      or numSubfields <> state.numSubfields;
         error (parm : state
              : errorCodes.nestedStructNotAllowed.code
              : errorCodes.nestedStructNotAllowed.msg);
      endif;

      return;
   endif;

   state.dataStructureName = parm.name;            //  3 
   state.numSubfields = numSubfields;
   state.haveHeader = *ON;

   writeLine (parm                                 //  4 
            : '<table border="1">');

   if state.haveCaption;
      writeLine (parm : '<caption>' : *on);
      QrnDgAddText (parm.handle
                  : %addr(state.caption : *data)
                  : %len(state.caption));
      writeLine (parm : '</caption>');
   endif;

   writeLine (parm
            : '<thead>'
            + '<tr>');

   FOR i = 1 TO numSubfields;                      //  5 
      writeLine (parm
              : '<td><b>'
              : *ON); // skip the newline for this output
      genColumnName (parm
                   : state
                   : QrnDgGetSubfieldName (parm.handle : i));  //  6 
      writeLine (parm : '</b></td>');
   ENDFOR;

   writeLine (parm                                 //  7 
            : '</tr>'
            + '</thead>'
            + '<tbody>');

END-PROC genStartTable;

genColumnName 程序

此程序會從子欄位名稱產生直欄名稱。

請注意程序的下列層面:

  1. 此程序預期子欄位以底線區隔其字組,且表格所需的字組大寫; 例如 Column_Heading。 若要建立直欄名稱,此程序會將底線變更為空格。

    請參閱使用此 DATA-GEN 產生器的 程式 程式碼,以查看它如何定義子欄位名稱。

  2. columnName 變數是可變長度 UTF-16 變數,因此產生器可以使用 QrnDgAddText 回呼程序來產生直欄的名稱。

DCL-PROC genColumnName;
   DCL-PI *n extproc(*dclcase);
      parm LIKEDS(QrnDgParm_t);
      state LIKEDS(state_t);
      name like(QrnDgName_T) const;
   END-PI;
   DCL-S columnName LIKE(name);
   DCL-C underscore %UCS2('_');
   DCL-C BLANK %UCS2(' ');

   columnName = %XLATE(UNDERSCORE : BLANK : name);       //  1 

   QrnDgAddText (parm.handle                             //  2 
               : %ADDR(columnName : *DATA)
               : %LEN(columnName));
END-PROC genColumnName;

genEndTable 程序

此程序會產生 HTML 表格的結尾。


DCL-PROC genEndTable;
   DCL-PI *n extproc(*dclcase);
      parm LIKEDS(QrnDgParm_t);
      state LIKEDS(state_t);
   END-PI;

   writeLine (parm
            : '</tbody>'
            + '</table>');
END-PROC genEndTable;

writeLine 程序

此程序會以工作 CCSID 產生文字。

請注意程序的下列層面:

  1. 產生器會決定它是否應該在產生文字之後產生新行。 它首先使用傳遞至產生器之參數的 outputIsToFile 子欄位,以判定產生的文字是否用於串流檔。 如果沒有,則不需要換行。

    不過,呼叫程式可能已指出此程序不應在選用 skipNewLine 參數中產生換行。

  2. 由於文字是在工作 CCSID 中,因此產生器可以使用簡式 QrnDgAddTextString 回呼程序來輸出文字。
  3. 產生器會使用 QrnDgAddTextsNewLine 回呼程序來新增換行字元。

DCL-PROC writeLine;
   DCL-PI *N EXTPROC(*DCLCASE);
      parm LIKEDS(QrnDgParm_t);
      line pointer VALUE OPTIONS(*STRING);
      skipNewLine IND CONST OPTIONS(*NOPASS);
   END-PI;
   DCL-S doNewLine IND INZ(*ON);

   doNewLine = parm.outputIsToFile;                //  1 
   IF %PARMS() >= %PARMNUM(skipNewLine)
   AND skipNewLine;
      doNewLine = *OFF;
   ENDIF;

   QrnDgAddTextString (parm.handle : line);        //  2 
   IF doNewLine;
      QrnDgAddTextNewline (parm.handle);           //  3 
   ENDIF;
END-PROC writeLine;

error 程序

此程序會產生錯誤狀況,這會導致 DATA-GEN 作業失敗。

請注意程序的下列層面:

  1. 此程序會先使用 QrnDgTrace 回呼程序來輸出追蹤訊息。 如果使用者正在追蹤 DATA-GEN 作業,則追蹤訊息將說明追蹤中接下來出現的錯誤碼。
  2. QrnDgReportError 回呼程序用來報告錯誤狀況。 對 nested 參數傳遞 *ON ,指出追蹤訊息應該巢狀內嵌在追蹤中的其他資訊內。

    在呼叫 QrnDgReportError 程序之後,控制項將不會回到此程序。 將再次針對最終 QrnDgEvent_12_Terminate 事件呼叫產生器。


DCL-PROC error;
   DCL-PI *n extproc(*dclcase);
      parm LIKEDS(QrnDgParm_t);
      state LIKEDS(state_t);
      errorCode INT(10) VALUE;
      errorMessage VARCHAR(100) CONST;
   END-PI;

   QrnDgTrace (parm.handle                         //  1 
             : errorMessage
             : '1');

   QrnDgReportError (parm.handle                   //  2 
                   : errorCode);
   // Control will not return here

END-PROC error;

SQL 陳述式,用來建立範例所使用的檔案


CREATE TABLE QGPL/MYORDERS
   (NAME VARCHAR (25 ) NOT NULL WITH DEFAULT,
   ITEMTYPE VARCHAR (25 ) NOT NULL WITH DEFAULT,
   ITEMPRICE DECIMAL (9 , 2) NOT NULL WITH DEFAULT)

INSERT INTO QGPL/MYORDERS
    VALUES('Refrigerator', 'Appliance', 525.95)

INSERT INTO QGPL/MYORDERS
    VALUES('Shirt', 'Clothing', 5.95)

INSERT INTO QGPL/MYORDERS
    VALUES('Rake', 'Gardening', 15.95)

範例中 DATA-GEN 作業產生的輸出

以下是 DATA-GEN 序列所產生的 HTML 表格。


<table border="1">
<caption>Order 2019-11-15</caption>
<thead><tr>
<td><b>Name</b></td>
<td><b>Type</b></td>
<td><b>Item Price</b></td>
</tr></thead><tbody>
<tr>
<td>Refrigerator</td>
<td>Appliance</td>
<td>525.95</td>
</tr>
<tr>
<td>Shirt</td>
<td>Clothing</td>
<td>5.95</td>
<td>5.95</td>
</tr>
<tr>
<td>Rake</td>
<td>Gardening</td>
<td>15.95</td>
</tr>
</tbody></table>

下表顯示表格的顯示方式:

表 1. x
名稱 類型 項目價格
冰箱 設備 525.95
襯衫 服裝 5.95
園藝 15.95

下列顯示程式中最終 DATA-GEN 作業之後的 customerTable 變數值,如除錯器中所示。


> EVAL customerTable
  CUSTOMERTABLE =
            ....5...10...15...20...25...30...35...40...45...50...55...60
       1   '<table border="1"><thead><tr><td><b>Name</b></td><td><b>Addr'
      61   'ess</b></td><td><b>Zip Code</b></td></tr></thead><tbody><tr>'
     121   '<td>A. Smith</td><td>123 Elm Street</td><td>11111</td></tr><'
     181   '/tbody></table>                                             '

下表顯示表格的顯示方式:

表 2. x
名稱 地址 郵遞區號
A. Smith 123 Elm Street 11111
變更結束