├── README.md ├── .gitignore ├── src └── gz │ └── jythonhelper │ ├── JythonHelperConfig.java │ ├── JythonHelperConfigForm.form │ ├── JythonHelperAction.java │ ├── JythonHelperConfigForm.java │ └── EasyJython.java ├── resources └── META-INF │ └── plugin.xml └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | JythonHelper 2 | ============ 3 | 4 | Generate Python skeleton for Java classes in Jython projects. 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | 14 | #Intellij settings 15 | *.iml 16 | .idea/ 17 | out/ 18 | -------------------------------------------------------------------------------- /src/gz/jythonhelper/JythonHelperConfig.java: -------------------------------------------------------------------------------- 1 | package gz.jythonhelper; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.ServiceManager; 5 | import com.intellij.openapi.components.State; 6 | import com.intellij.openapi.project.Project; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | /** 10 | * Created by IntelliJ IDEA. 11 | * User: gongze 12 | * Date: 12/3/13 13 | * Time: 2:24 PM 14 | */ 15 | @State(name = "JythonHelperConfig", storages = {@com.intellij.openapi.components.Storage(file = "$WORKSPACE_FILE$")}) 16 | public class JythonHelperConfig implements PersistentStateComponent { 17 | public static class State { 18 | public String javaLibs[] = new String[0]; 19 | } 20 | 21 | State myState = new State(); 22 | 23 | @Nullable 24 | @Override 25 | public State getState() { 26 | return myState; 27 | } 28 | 29 | @Override 30 | public void loadState(State state) { 31 | myState = state; 32 | } 33 | 34 | public static JythonHelperConfig getConfig(Project project) { 35 | return ServiceManager.getService(project, JythonHelperConfig.class); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | JythonHelper 3 | JythonHelper 4 | 1.3 5 | Kane 6 | 7 | com.intellij.modules.python 8 | 9 | Goal:Generate Python skeleton for Java classes in Jython projects. 10 | Usage: Add skeleton output directory in setting: 'Jython Helper' and Java libraries path(jars). Then apply 11 | 'Jython Helper' by right clicking for single Jython file or directory containing Jython files. 12 | Code at github: https://github.com/kaneg/JythonHelper 13 | 14 | 15 | 1.3: Support latest IDEA/PyCharm. 16 | 1.2: No longer needs to manually set Target Directory. All skeletons can be generated into SDK's binary folder. 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/gz/jythonhelper/JythonHelperConfigForm.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
66 | -------------------------------------------------------------------------------- /src/gz/jythonhelper/JythonHelperAction.java: -------------------------------------------------------------------------------- 1 | package gz.jythonhelper; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 6 | import com.intellij.openapi.module.Module; 7 | import com.intellij.openapi.module.ModuleUtil; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.openapi.projectRoots.Sdk; 10 | import com.intellij.openapi.ui.Messages; 11 | import com.intellij.openapi.vfs.LocalFileSystem; 12 | import com.intellij.openapi.vfs.VirtualFile; 13 | import com.jetbrains.python.sdk.PythonSdkUtil; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | /** 17 | * Created by IntelliJ IDEA. 18 | * User: gongze 19 | * Date: 12/3/13 20 | * Time: 10:37 AM 21 | */ 22 | public class JythonHelperAction extends AnAction { 23 | public void actionPerformed(@NotNull AnActionEvent event) { 24 | final Project project = event.getData(PlatformDataKeys.PROJECT); 25 | final VirtualFile[] vFiles = event.getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY); 26 | if (vFiles == null || vFiles.length == 0) { 27 | Messages.showWarningDialog("No file to be review", "Error"); 28 | return; 29 | } 30 | VirtualFile target = vFiles[0]; 31 | JythonHelperConfig config = JythonHelperConfig.getConfig(project); 32 | JythonHelperConfig.State state = config.getState(); 33 | String targetDirectory = getTargetDirectory(target, project); 34 | if (targetDirectory == null) { 35 | return; 36 | } 37 | String[] javaLibs = state.javaLibs; 38 | generatePy(targetDirectory, javaLibs, target.getPath()); 39 | Messages.showInfoMessage("Python files have been generated in directory:" + targetDirectory, "Info"); 40 | LocalFileSystem.getInstance().refresh(false); 41 | } 42 | 43 | private String getTargetDirectory(VirtualFile vf, Project project) { 44 | Module module = ModuleUtil.findModuleForFile(vf, project); 45 | Sdk sdk = PythonSdkUtil.findPythonSdk(module); 46 | if (sdk == null) { 47 | Messages.showErrorDialog(project, "No SDK FOUND", "No SDK FOUND"); 48 | return null; 49 | } 50 | return PythonSdkUtil.getSkeletonsPath(sdk); 51 | } 52 | 53 | private void generatePy(String outputDir, String[] libs, String... files) { 54 | EasyJython ej = new EasyJython(outputDir); 55 | ej.setLibs(libs); 56 | ej.generatePys(files); 57 | } 58 | 59 | @Override 60 | public void update(AnActionEvent event) { 61 | final VirtualFile[] vFiles = event.getData(PlatformDataKeys.VIRTUAL_FILE_ARRAY); 62 | setActionEnable(event, vFiles != null && 1 == vFiles.length && 63 | (vFiles[0].isDirectory() || "py".equalsIgnoreCase(vFiles[0].getExtension()))); 64 | } 65 | 66 | private void setActionEnable(AnActionEvent event, boolean isEnable) { 67 | event.getPresentation().setEnabled(isEnable); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/gz/jythonhelper/JythonHelperConfigForm.java: -------------------------------------------------------------------------------- 1 | package gz.jythonhelper; 2 | 3 | import com.intellij.openapi.fileChooser.FileChooser; 4 | import com.intellij.openapi.fileChooser.FileChooserDescriptor; 5 | import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; 6 | import com.intellij.openapi.options.Configurable; 7 | import com.intellij.openapi.options.ConfigurationException; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.openapi.vfs.VirtualFile; 10 | import com.intellij.openapi.vfs.VirtualFileManager; 11 | import com.intellij.util.Consumer; 12 | import org.jetbrains.annotations.Nls; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | import javax.swing.*; 16 | import java.awt.event.ActionEvent; 17 | import java.awt.event.ActionListener; 18 | import java.util.List; 19 | 20 | /** 21 | * Created by IntelliJ IDEA. 22 | * User: gongze 23 | * Date: 12/3/13 24 | * Time: 3:33 PM 25 | */ 26 | public class JythonHelperConfigForm implements Configurable { 27 | public static final String JYTHON_HELPER = "Jython Helper"; 28 | private JPanel mainPane; 29 | private JList libList; 30 | private JButton addLibButton; 31 | private JButton removeLibButton; 32 | private Project project; 33 | 34 | public JythonHelperConfigForm(final Project project) { 35 | this.project = project; 36 | addLibButton.addActionListener(new ActionListener() { 37 | @Override 38 | public void actionPerformed(ActionEvent e) { 39 | FileChooserDescriptor fcd = FileChooserDescriptorFactory.createMultipleJavaPathDescriptor(); 40 | FileChooser.chooseFiles(fcd, project, null, new Consumer>() { 41 | @Override 42 | public void consume(List virtualFiles) { 43 | DefaultListModel model = (DefaultListModel) JythonHelperConfigForm.this.libList.getModel(); 44 | for (VirtualFile virtualFile : virtualFiles) { 45 | model.addElement(virtualFile.getPresentableUrl()); 46 | } 47 | } 48 | }); 49 | } 50 | }); 51 | removeLibButton.addActionListener(new ActionListener() { 52 | @Override 53 | public void actionPerformed(ActionEvent e) { 54 | int[] sis = libList.getSelectedIndices(); 55 | if (sis.length > 0) { 56 | DefaultListModel model = (DefaultListModel) JythonHelperConfigForm.this.libList.getModel(); 57 | for (int i = sis.length - 1; i >= 0; i--) { 58 | model.remove(sis[i]); 59 | } 60 | } 61 | } 62 | }); 63 | } 64 | 65 | @Nls 66 | @Override 67 | public String getDisplayName() { 68 | return JYTHON_HELPER; 69 | } 70 | 71 | @Nullable 72 | @Override 73 | public String getHelpTopic() { 74 | return null; 75 | } 76 | 77 | @Nullable 78 | @Override 79 | public JComponent createComponent() { 80 | return mainPane; 81 | } 82 | 83 | @Override 84 | public boolean isModified() { 85 | return true; 86 | } 87 | 88 | @Override 89 | public void apply() throws ConfigurationException { 90 | JythonHelperConfig.State myState = getState(); 91 | ListModel model = this.libList.getModel(); 92 | int size = model.getSize(); 93 | String[] values = new String[size]; 94 | for (int i = 0; i < values.length; i++) { 95 | values[i] = model.getElementAt(i).toString(); 96 | } 97 | myState.javaLibs = values; 98 | } 99 | 100 | private JythonHelperConfig.State getState() { 101 | JythonHelperConfig config = JythonHelperConfig.getConfig(project); 102 | return config.getState(); 103 | } 104 | 105 | @Override 106 | public void reset() { 107 | JythonHelperConfig.State myState = getState(); 108 | DefaultListModel model = new DefaultListModel(); 109 | String[] values = new String[0]; 110 | if (myState.javaLibs != null) { 111 | values = myState.javaLibs; 112 | } 113 | for (String value : values) { 114 | model.addElement(value); 115 | } 116 | libList.setModel(model); 117 | } 118 | 119 | @Override 120 | public void disposeUIResources() { 121 | 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /src/gz/jythonhelper/EasyJython.java: -------------------------------------------------------------------------------- 1 | package gz.jythonhelper; 2 | 3 | import java.io.*; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Method; 6 | import java.lang.reflect.Modifier; 7 | import java.net.MalformedURLException; 8 | import java.net.URL; 9 | import java.net.URLClassLoader; 10 | import java.util.*; 11 | import java.util.regex.Matcher; 12 | import java.util.regex.Pattern; 13 | 14 | /** 15 | * Created by IntelliJ IDEA. 16 | * User: gongze 17 | * Date: 1/30/13 18 | * Time: 2:52 PM 19 | */ 20 | public class EasyJython { 21 | 22 | public static final Pattern PATTERN_FROM_IMPORT = Pattern.compile("\\s*from\\s*(\\S+)\\s*import\\s+(\\w[\\w,\\p{Blank}]*)"); 23 | 24 | public static final Pattern PATTERN_IMPORT = Pattern.compile("\\s*import\\s*(\\S+)"); 25 | 26 | public static final Pattern PATTERN_FROM_IMPORT_MULTIPLE = Pattern.compile("\\s*from\\s*(\\S+)\\s*import\\s+\\(\\s*(.*)\\s*\\)", Pattern.MULTILINE); 27 | 28 | public static final String[] PREDEFINED_CLASSES = {}; 29 | 30 | ClassGenerator cg; 31 | private ClassLoader classLoader; 32 | 33 | public EasyJython(String outputDir) { 34 | cg = new PyClassGenerator(outputDir); 35 | } 36 | 37 | public void setLibs(String[] libs) { 38 | List urls = new ArrayList(); 39 | for (String name : libs) { 40 | try { 41 | if (name.endsWith("*")) { 42 | name = name.substring(0, name.length() - 1); 43 | } 44 | File file = new File(name); 45 | if (file.isDirectory()) { 46 | File[] files = file.listFiles(new FileFilter() { 47 | @Override 48 | public boolean accept(File pathname) { 49 | return pathname.isFile() && pathname.getName().toLowerCase().endsWith(".jar"); 50 | } 51 | }); 52 | for (File f : files) { 53 | urls.add(f.toURI().toURL()); 54 | } 55 | } else { 56 | urls.add(file.toURI().toURL()); 57 | } 58 | } catch (MalformedURLException e) { 59 | e.printStackTrace(); 60 | } 61 | } 62 | classLoader = new URLClassLoader(urls.toArray(new URL[urls.size()])); 63 | } 64 | 65 | private static String[] getFilesFromArgs(String[] args) { 66 | List list = new ArrayList(); 67 | for (String arg : args) { 68 | String[] split = arg.split(";|:"); 69 | list.addAll(Arrays.asList(split)); 70 | } 71 | return list.toArray(new String[list.size()]); 72 | } 73 | 74 | public void generatePys(String... pyFiles) { 75 | Set classList = new HashSet(); 76 | classList.addAll(Arrays.asList(PREDEFINED_CLASSES)); 77 | for (String f : pyFiles) { 78 | File file = new File(f); 79 | if (file.isFile()) { 80 | System.out.println("File:--->" + file.getName()); 81 | classList.addAll(getClassListFromPy(file)); 82 | } else { 83 | System.out.println("Directory:--->" + file.getAbsolutePath()); 84 | File[] files = file.listFiles(new FilenameFilter() { 85 | @Override 86 | public boolean accept(File dir, String name) { 87 | return name.endsWith(".py"); 88 | } 89 | }); 90 | if (files != null) { 91 | for (File child : files) { 92 | System.out.println("--->" + child.getName()); 93 | classList.addAll(getClassListFromPy(child)); 94 | } 95 | } 96 | } 97 | } 98 | 99 | for (String className : classList) { 100 | try { 101 | System.out.println("Generating:" + className); 102 | Class clazz = Class.forName(className, false, classLoader); 103 | System.out.println(clazz); 104 | cg.createPyForClass(clazz); 105 | } catch (ClassNotFoundException e) { 106 | System.err.println("Class not found:" + e.getMessage()); 107 | } catch (IOException e) { 108 | System.err.println("IO Exception:" + e.getMessage()); 109 | } 110 | } 111 | } 112 | 113 | private Set getClassListFromPy(File file) { 114 | StringBuilder sb = new StringBuilder(); 115 | Set classes = new HashSet(); 116 | try { 117 | String line; 118 | BufferedReader br = new BufferedReader(new FileReader(file)); 119 | while ((line = br.readLine()) != null) { 120 | sb.append(line).append("\n"); 121 | Matcher matcher = PATTERN_FROM_IMPORT.matcher(line); 122 | if (matcher.matches()) { 123 | System.out.println(line); 124 | String packageName = matcher.group(1); 125 | String classSimpleName = matcher.group(2); 126 | String[] names = classSimpleName.split(",");//for from xyz import Abc, Def, Ghi 127 | for (String className : names) { 128 | className = className.trim(); 129 | if (!className.isEmpty()) { 130 | String[] split = className.split(" ");// for import Abc as abc 131 | className = split[0].trim(); 132 | classes.add(packageName + "." + className); 133 | } 134 | } 135 | continue; 136 | } 137 | 138 | matcher = PATTERN_IMPORT.matcher(line); 139 | if (matcher.matches()) { 140 | String classSimpleName = matcher.group(1); 141 | classes.add(classSimpleName); 142 | } 143 | } 144 | br.close(); 145 | Matcher matcher = PATTERN_FROM_IMPORT_MULTIPLE.matcher(sb.toString()); 146 | while (matcher.find()) { 147 | String packageName = matcher.group(1); 148 | String classSimpleName = matcher.group(2); 149 | String[] names = classSimpleName.split(",");//for from xyz import Abc, Def, Ghi 150 | for (String className : names) { 151 | className = className.trim(); 152 | if (!className.isEmpty()) { 153 | String[] split = className.split(" ");// for import Abc as abc 154 | className = split[0].trim(); 155 | classes.add(packageName + "." + className); 156 | } 157 | } 158 | } 159 | } catch (IOException e) { 160 | e.printStackTrace(); 161 | } 162 | return classes; 163 | } 164 | } 165 | 166 | abstract class ClassGenerator { 167 | public static final String INIT_PY = "__init__.py"; 168 | public static final String DEF_TPL = "def %s(%s):"; 169 | public static final String CLASS_TPL = "class %s:"; 170 | public static final String PASS = "pass"; 171 | private static final Object INDENT = " "; 172 | public static final String STATIC_METHOD = "@staticmethod"; 173 | 174 | public static final String LINE_SEPARATOR = System.getProperty("line.separator"); 175 | public static final Set IGNORED_FIELDS = new HashSet(Arrays.asList(new String[]{ 176 | "in" 177 | })); 178 | public static final Set IGNORED_METHODS = new HashSet(Arrays.asList(new String[] 179 | {"wait", "toString", "hashCode", "notify", "notifyAll", "getClass", "yield"})); 180 | 181 | private String outputDir; 182 | public static final String INIT_TEMPLATE = "# encoding: utf-8\n" + 183 | "# module %s\n" + 184 | "# from (built-in)\n" + 185 | "# by generator 999.999\n" + 186 | "# source:%s\n"; 187 | 188 | protected ClassGenerator(String outputDir) { 189 | this.outputDir = outputDir; 190 | } 191 | 192 | String indents(String line, int i) { 193 | String[] split = line.split(LINE_SEPARATOR); 194 | StringBuilder sb = new StringBuilder(); 195 | for (String s : split) { 196 | sb.append(indent(s, i)); 197 | } 198 | return sb.toString(); 199 | } 200 | 201 | 202 | String indent(String line) { 203 | return indent(line, 1); 204 | } 205 | 206 | String indent(String line, int level) { 207 | StringBuilder sb = new StringBuilder(); 208 | for (int i = 0; i < level; i++) { 209 | sb.append(INDENT); 210 | } 211 | sb.append(line); 212 | return sb.toString(); 213 | } 214 | 215 | String makeAField(Field f) { 216 | return f.getName() + " = None"; 217 | } 218 | 219 | String indents(String line) { 220 | String[] split = line.split(LINE_SEPARATOR); 221 | StringBuilder sb = new StringBuilder(); 222 | for (String s : split) { 223 | sb.append(indent(s, 1)); 224 | sb.append(LINE_SEPARATOR); 225 | } 226 | return sb.toString(); 227 | } 228 | 229 | void writePyHeader(FileWriter fw, String name, URL resourceURL) { 230 | String source = ""; 231 | if (resourceURL != null) { 232 | source = resourceURL.toString(); 233 | } 234 | try { 235 | fw.write(String.format(INIT_TEMPLATE, name, source)); 236 | } catch (IOException e) { 237 | e.printStackTrace(); 238 | } 239 | } 240 | 241 | void ensureInitPy(File dir) { 242 | File initFile = new File(dir, INIT_PY); 243 | // if (initFile.exists() && initFile.lastModified() < startTime) { 244 | // initFile.delete(); 245 | // } 246 | if (!initFile.exists()) { 247 | try { 248 | boolean newFile = initFile.createNewFile(); 249 | if (newFile) { 250 | FileWriter fw = new FileWriter(initFile); 251 | writePyHeader(fw, initFile.getName(), null); 252 | fw.close(); 253 | } 254 | } catch (IOException e) { 255 | e.printStackTrace(); 256 | } 257 | } 258 | } 259 | 260 | File createDirectory(Class clazz) { 261 | String packageName = clazz.getPackage().getName(); 262 | System.out.println("create package directory:" + packageName); 263 | 264 | String[] split = packageName.split("\\."); 265 | File currentDir = new File(outputDir); 266 | for (String dirName : split) { 267 | File tmpDir = new File(currentDir, dirName); 268 | if (!tmpDir.exists()) { 269 | tmpDir.mkdir(); 270 | } 271 | ensureInitPy(tmpDir); 272 | currentDir = tmpDir; 273 | } 274 | return currentDir; 275 | } 276 | 277 | Method[] filterOverrideMethods(Method[] methods) { 278 | Map methodMap = new HashMap(); 279 | 280 | for (Method m : methods) { 281 | String name = m.getName(); 282 | Method existingMethod = methodMap.get(name); 283 | if (existingMethod == null) { 284 | methodMap.put(name, m); 285 | } else { 286 | Class[] parameterTypes = existingMethod.getParameterTypes(); 287 | Class[] newParameterTypes = m.getParameterTypes(); 288 | if (newParameterTypes.length > parameterTypes.length) { 289 | methodMap.put(name, m); 290 | } 291 | } 292 | } 293 | return methodMap.values().toArray(new Method[methodMap.keySet().size()]); 294 | } 295 | 296 | public abstract void createPyForClass(Class clazz) throws IOException; 297 | } 298 | 299 | class PyClassGenerator extends ClassGenerator { 300 | 301 | Pattern CLASS_NAME_PATTERN = Pattern.compile("class\\s+(\\w+).*:"); 302 | 303 | protected PyClassGenerator(String outputDir) { 304 | super(outputDir); 305 | } 306 | 307 | public void createPyForClass(Class clazz) throws IOException { 308 | File directory = createDirectory(clazz); 309 | String classContent = generateClassAsPyClass(clazz); 310 | File initPy = new File(directory, INIT_PY); 311 | Map classStringMap = readClassesFromInitPy(initPy); 312 | classStringMap.put(clazz.getSimpleName(), new StringBuilder(classContent)); 313 | String name = clazz.getName().replace('.', '/') + ".class"; 314 | ClassLoader classLoader = clazz.getClassLoader(); 315 | URL resourceURL = null; 316 | if (classLoader != null) { 317 | resourceURL = classLoader.getResource(name); 318 | } 319 | writeClassesFromInitPy(classStringMap, initPy, resourceURL); 320 | } 321 | 322 | public void createPyForClass0(Class clazz) throws IOException { 323 | File directory = createDirectory(clazz); 324 | String classContent = generateClassAsPyClass(clazz); 325 | File classFile = new File(directory, INIT_PY); 326 | FileWriter fileWriter = new FileWriter(classFile, true); 327 | fileWriter.write(classContent); 328 | fileWriter.close(); 329 | } 330 | 331 | private Map readClassesFromInitPy(File initFile) throws IOException { 332 | BufferedReader br = new BufferedReader(new FileReader(initFile)); 333 | String line; 334 | boolean inClass = false; 335 | Map maps = new HashMap(); 336 | StringBuilder sb = null; 337 | while ((line = br.readLine()) != null) { 338 | if (line.startsWith("class")) { 339 | inClass = true; 340 | Matcher matcher = CLASS_NAME_PATTERN.matcher(line); 341 | if (matcher.matches()) { 342 | String className = matcher.group(1); 343 | sb = new StringBuilder(); 344 | maps.put(className, sb); 345 | } 346 | } 347 | if (inClass && sb != null) { 348 | sb.append(line).append(LINE_SEPARATOR); 349 | } 350 | } 351 | br.close(); 352 | return maps; 353 | } 354 | 355 | private void writeClassesFromInitPy(Map classStringMap, File initPy, URL resourceURL) throws IOException { 356 | FileWriter fileWriter = new FileWriter(initPy); 357 | writePyHeader(fileWriter, initPy.getName(), resourceURL); 358 | for (StringBuilder classContent : classStringMap.values()) { 359 | fileWriter.write(classContent.toString()); 360 | } 361 | fileWriter.close(); 362 | } 363 | 364 | private String generateClassAsPyClass(Class clazz) { 365 | StringBuilder sb = new StringBuilder(); 366 | sb.append(String.format(CLASS_TPL, clazz.getSimpleName())); 367 | sb.append(LINE_SEPARATOR); 368 | Field[] fields = new Field[0]; 369 | try { 370 | fields = clazz.getFields(); 371 | } catch (SecurityException e) { 372 | e.printStackTrace(); 373 | } 374 | for (Field f : fields) { 375 | int modifiers = f.getModifiers(); 376 | if (Modifier.isPublic(modifiers)) { 377 | if (!IGNORED_FIELDS.contains(f.getName())) { 378 | sb.append(indent(makeAField(f))); 379 | sb.append(LINE_SEPARATOR); 380 | } 381 | } 382 | } 383 | sb.append(LINE_SEPARATOR); 384 | Method[] methods = new Method[0]; 385 | try { 386 | methods = clazz.getMethods(); 387 | } catch (SecurityException e) { 388 | e.printStackTrace(); 389 | } 390 | methods = filterOverrideMethods(methods); 391 | for (Method m : methods) { 392 | int modifiers = m.getModifiers(); 393 | if (Modifier.isPublic(modifiers)) { 394 | String name = m.getName(); 395 | if (!IGNORED_METHODS.contains(name)) { 396 | sb.append(indents(generateMethodForPyClass(m))); 397 | sb.append(indents(PASS, 2)); 398 | sb.append(LINE_SEPARATOR); 399 | sb.append(LINE_SEPARATOR); 400 | } 401 | } 402 | } 403 | return sb.toString(); 404 | } 405 | 406 | private String generateMethodForPyClass(Method m) { 407 | Class[] parameterTypes = m.getParameterTypes(); 408 | StringBuilder sb = new StringBuilder(); 409 | int modifiers = m.getModifiers(); 410 | String prefix = ""; 411 | if (Modifier.isStatic(modifiers)) { 412 | prefix = STATIC_METHOD + LINE_SEPARATOR; 413 | } else { 414 | sb.append("self, "); 415 | } 416 | for (int i = 0; i < parameterTypes.length; i++) { 417 | Class type = parameterTypes[i]; 418 | String typeName; 419 | if (type.isArray()) { 420 | typeName = type.getComponentType().getSimpleName(); 421 | } else { 422 | typeName = type.getSimpleName(); 423 | } 424 | sb.append(typeName + (i > 0 ? i : "") + "=None"); 425 | if (i < parameterTypes.length - 1) { 426 | sb.append(", "); 427 | } 428 | } 429 | 430 | return prefix + String.format(DEF_TPL, m.getName(), sb.toString()); 431 | } 432 | } 433 | 434 | class PyModuleGenerator extends ClassGenerator { 435 | protected PyModuleGenerator(String outputDir) { 436 | super(outputDir); 437 | } 438 | 439 | public void createPyForClass(Class clazz) throws IOException { 440 | File directory = createDirectory(clazz); 441 | String classContent = generateClassAsModule(clazz); 442 | if (classContent == null) { 443 | return; 444 | } 445 | File classFile = new File(directory, clazz.getSimpleName() + ".py"); 446 | FileWriter fileWriter = new FileWriter(classFile); 447 | fileWriter.write(classContent); 448 | fileWriter.close(); 449 | } 450 | 451 | private String generateMethodForModule(Method m) { 452 | Class[] parameterTypes = m.getParameterTypes(); 453 | StringBuilder sb = new StringBuilder(); 454 | int modifiers = m.getModifiers(); 455 | String prefix = ""; 456 | for (int i = 0; i < parameterTypes.length; i++) { 457 | Class type = parameterTypes[i]; 458 | String typeName; 459 | if (type.isArray()) { 460 | typeName = type.getComponentType().getSimpleName(); 461 | } else { 462 | typeName = type.getSimpleName(); 463 | } 464 | sb.append(typeName + (i > 0 ? i : "") + "=None"); 465 | if (i < parameterTypes.length - 1) { 466 | sb.append(", "); 467 | } 468 | } 469 | 470 | return prefix + String.format(DEF_TPL, m.getName(), sb.toString()); 471 | } 472 | 473 | 474 | private String generateClassAsModule(Class clazz) { 475 | StringBuilder sb = new StringBuilder(); 476 | Field[] fields = new Field[0]; 477 | 478 | try { 479 | fields = clazz.getFields(); 480 | } catch (Throwable e) { 481 | System.err.println("Get Field error:" + e.getMessage()); 482 | } 483 | for (Field f : fields) { 484 | int modifiers = f.getModifiers(); 485 | if (Modifier.isPublic(modifiers)) { 486 | if (!IGNORED_FIELDS.contains(f.getName())) { 487 | sb.append(indent(makeAField(f), 0)); 488 | sb.append(LINE_SEPARATOR); 489 | } 490 | } 491 | } 492 | sb.append(LINE_SEPARATOR); 493 | sb.append(LINE_SEPARATOR); 494 | Method[] methods = new Method[0]; 495 | try { 496 | methods = clazz.getMethods(); 497 | } catch (Throwable e) { 498 | System.err.println("Get Method error:" + e.getMessage()); 499 | } 500 | methods = filterOverrideMethods(methods); 501 | for (Method m : methods) { 502 | int modifiers = m.getModifiers(); 503 | if (Modifier.isPublic(modifiers)) { 504 | String name = m.getName(); 505 | if (!IGNORED_METHODS.contains(name)) { 506 | sb.append(indents(generateMethodForModule(m), 0)); 507 | sb.append(LINE_SEPARATOR); 508 | sb.append(indents(PASS, 2)); 509 | sb.append(LINE_SEPARATOR); 510 | sb.append(LINE_SEPARATOR); 511 | sb.append(LINE_SEPARATOR); 512 | } 513 | } 514 | } 515 | return sb.toString(); 516 | } 517 | } --------------------------------------------------------------------------------