创建一个Java :: Geci生成器
幾天前,我寫了有關Java :: Geci架構,代碼生成原理以及生成Java源代碼的可能不同方式的文章。
在本文中,我將討論在Java :: Geci中創建生成器有多么簡單。
您好,Wold生成器
HelloWorld1
最簡單的生成器是Hello, World! 發電機。 這將生成一個打印Hello, World!的方法Hello, World! 到標準輸出。 要創建此生成器,Java類必須實現Generator接口。 生成器的整個代碼為:
package javax0.geci.tutorials.hello;import javax0.geci.api.GeciException; import javax0.geci.api.Generator; import javax0.geci.api.Source;public class HelloWorldGenerator1 implements Generator {public void process(Source source) {try {final var segment = source.open("hello");segment.write_r("public static void hello(){");segment.write("System.out.println(\"Hello, World\");");segment.write_l("}");} catch (Exception e) {throw new GeciException(e);}} }這確實是整個生成器類。 沒有簡化或刪除的行。 當框架找到需要方法hello()的文件時,它將調用process() 。
方法process ()查詢名為“ hello”的段。 這是指線
//<editor-fold id="hello">//</editor-fold>在源代碼中。 segment對象可用于將行寫入代碼。 write()方法write()一行。 方法write_r()也會寫一行,但是它也表示必須縮進該行之后的行。 相反的是write_l() ,該信號指示已經將此行和連續的行重新制表回到先前的位置。
要使用生成器,我們應該有一個需要它的類。 這是
package javax0.geci.tutorials.hello;public class HelloWorld1 {//<editor-fold id="hello">//</editor-fold> }我們還需要一個測試,該測試將在每次編譯代碼并運行單元測試時運行代碼生成:
package javax0.geci.tutorials.hello;import javax0.geci.engine.Geci; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test;import static javax0.geci.api.Source.maven;public class TestHelloWorld1 {@Test@DisplayName("Start code generator for HelloWorld1")void testGenerateCode() throws Exception {Assertions.assertFalse(new Geci().only("^.*/HelloWorld1.java$").register(new HelloWorldGenerator1()).generate(), Geci.FAILED);} }執行代碼后,將修改HelloWorld1.java文件,并將在編輯器折疊之間插入以下行:
package javax0.geci.tutorials.hello;public class HelloWorld1 {//<editor-fold id="hello">public static void hello(){System.out.println("Hello, World");}//</editor-fold> }這是一個非常簡單的示例,我們可以進一步發展。
HelloWorld2
該示例中低于標準的一件事是,生成器的范圍在調用only()方法的測試中受到限制。 更好的做法是讓框架掃描所有文件并選擇本身以某種方式表明它們需要生成器服務的源文件。 在“你好,世界!”的情況下 生成器,它可以是hello段的存在,作為源代碼中的編輯器折疊。 如果存在,則代碼需要方法hello() ,否則不需要。 我們可以通過這種方式實現生成器的第二個版本。 我們還修改了實現,而不僅是實現接口Generator還擴展了抽象類AbstractGeneratorEx 。 名稱中的后綴Ex表示該類為我們處理異常。 這個抽象類實現方法process()并調用要定義的processEx() ,該簽名具有與process()相同的簽名,但允許拋出異常。 如果發生這種情況,則將其封裝在GeciException ,就像我們在第一個示例中所做的那樣。
該代碼將如下所示:
package javax0.geci.tutorials.hello;import javax0.geci.api.Source; import javax0.geci.tools.AbstractGeneratorEx;import java.io.IOException;public class HelloWorldGenerator2 extends AbstractGeneratorEx {public void processEx(Source source) throws IOException {final var segment = source.open("hello");if (segment != null) {segment.write_r("public static void hello(){");segment.write("System.out.println(\"Hello, World\");");segment.write_l("}");}} }盡管它正在檢查段的存在,但它甚至比第一個簡單。 當代碼調用source.open("hello") ,如果源代碼中沒有名為hello段,則該方法將返回null 。 使用第二個生成器的實際代碼與第一個相同。 當我們在代碼庫中運行兩個測試時,它們都會生成代碼,所幸的是相同的。
調用第二個生成器的測試是
package javax0.geci.tutorials.hello;import javax0.geci.engine.Geci; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test;import static javax0.geci.api.Source.maven;public class TestHelloWorld2 {@Test@DisplayName("Start code generator for HelloWorld2")void testGenerateCode() throws Exception {Assertions.assertFalse(new Geci().register(new HelloWorldGenerator2()).generate(), Geci.FAILED);} }請注意,這一次我們不需要限制調用only()方法的代碼掃描。 同樣, only(RegEx x)方法的文檔only(RegEx x)說,這是在生成器生成器的API中的最后選擇。
HelloWorld3
生成器的第一個版本和第二個版本正在處理文本文件,并且不使用我們修改的代碼實際上是Java的事實。 生成器的第三個版本將依賴于這一事實,這樣就可以創建一個生成器,可以在需要代碼生成的類中對其進行配置。
為此,我們可以擴展抽象類AbstractJavaGenerator 。 這個抽象類將找到與源代碼相對應的類,并且還將讀取該類的注釋中編碼的配置,我們將看到。 僅當源代碼是Java文件,已經編譯過的類(抱歉,編譯器,我們現在可以修改源代碼processEx() , processEx()的抽象類實現才調用process(Source source, Class klass, CompoundParams global)可能需要重新編譯),并且對該類進行了適當的注釋。
生成器代碼如下:
package javax0.geci.tutorials.hello;import javax0.geci.api.Source; import javax0.geci.tools.AbstractJavaGenerator; import javax0.geci.tools.CompoundParams;import java.io.IOException;public class HelloWorldGenerator3 extends AbstractJavaGenerator {public void process(Source source, Class<?> klass, CompoundParams global)throws IOException {final var segment = source.open(global.get("id"));final var methodName = global.get("methodName", "hello");segment.write_r("public static void %s(){", methodName);segment.write("System.out.println(\"Hello, World\");");segment.write_l("}");}public String mnemonic() {return "HelloWorld3";} }方法process() (接口中定義的方法的重載版本)獲取三個參數。 第一個是與第一個示例中非常相同的Source對象。 第二個是從我們正在處理的Java源文件創建的Class 。 第三個是框架從類注釋讀取的配置。 這也需要方法mnemonic()的支持。 這標識了生成器的名稱。 它是在配置中用作引用的字符串。 它必須是唯一的。
需要使用生成器進行修改的Java類必須使用Geci注釋進行注釋。 Geci注釋在庫javax0.geci.annotations.Geci定義。 用生成的代碼擴展的源代碼將如下所示:
package javax0.geci.tutorials.hello;import javax0.geci.annotations.Geci;@Geci("HelloWorld3 id='hallo' methodName='hiya'") public class HelloWorld3 {//<editor-fold id="hallo">//</editor-fold> }這里有點麻煩。 Java :: Geci是一個測試階段工具,對其的所有依賴項都是測試依賴項。 注釋庫是一個例外。 該庫必須是正常的依賴項,因為使用代碼生成的類都帶有此注釋,因此JVM將在運行時查找該注釋類,即使在運行時該注釋沒有作用。 因為JVM測試執行只是運行時,所以沒有區別。
要克服此Java :: Geci,您可以使用任何注釋,只要注釋接口的名稱為Geci且其value是String 。 這樣,我們可以通過以下方式使用第三個hello world生成器:
package javax0.geci.tutorials.hello;import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;@HelloWorld3a.Geci(value = "HelloWorld3 id='hallo'", methodName = "hiyaHuya") public class HelloWorld3a {//<editor-fold id="hallo">//</editor-fold>@Retention(RetentionPolicy.RUNTIME)@interface Geci {String value();String methodName() default "hello";} }請注意,在前面的示例中,參數id和methodName是在value字符串內定義的(如果未在注釋中定義任何其他參數,則這是默認參數)。 在這種情況下,很容易將參數拼寫錯誤,并且IDE不會僅僅因為IDE對配置Java :: Geci的字符串格式一無所知就不會為您提供任何支持。 另一方面,如果您有自己的注釋,則可以自由定義任何命名參數。 在此示例中,我們在接口中定義了方法methodName 。 Java :: Geci正在讀取注釋的參數以及解析參數的value字符串。 這樣,某些生成器可以使用自己的注釋,這些注釋可以幫助用戶定義定義為注釋參數的參數。
我們的第三個“ Hello,World!”的最后一個版本 應用程序可能是最簡單的:
package javax0.geci.tutorials.hello;import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;public class HelloWorld3b {//<editor-fold id="HelloWorld3" methodName = "hiyaNyunad">//</editor-fold> }類上沒有注釋,也沒有看起來像注釋的注釋。 唯一存在id HelloWorld3的editor-fold段是生成器的助記符。 如果存在,則AbstractJavaGenerator意識到這一點并從那里讀取參數。 (順便說一句:即使存在注釋,它也會讀取注釋中不存在的其他參數。)不僅讀取參數,還調用具體的實現,因此生成了代碼。 這種方法最簡單,可用于僅需要一個段即可將代碼生成到其中的代碼生成器,以及當它們不需要類中方法和字段的單獨配置選項時使用。
摘要
在本文中,我描述了如何編寫自己的生成器,并且還深入研究了如何使用注釋來配置需要生成代碼的類。 請注意,本文中討論的某些功能可能不在發行版中,但是您可以從https://github.com/verhas/javageci下載并構建(b)領先版本。
翻譯自: https://www.javacodegeeks.com/2019/05/creating-javageci-generator.html
總結
以上是生活随笔為你收集整理的创建一个Java :: Geci生成器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: junit测试设置不回滚_正确设置JUn
- 下一篇: java教程java自学_15必须阅读J