/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.zap.extension.api;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JavaAPIGenerator extends AbstractAPIGenerator {
/**
* The path of the package where the generated classes are deployed.
*/
private static final String TARGET_PACKAGE = "org/zaproxy/clientapi/gen";
/**
* Default output directory is the "gen" package of subproject zap-clientapi (of zap-api-java project).
*/
private static final String DEFAULT_OUTPUT_DIR = "../zap-api-java/subprojects/zap-clientapi/src/main/java/" + TARGET_PACKAGE;
private static final String HEADER =
"/* Zed Attack Proxy (ZAP) and its related class files.\n" +
" *\n" +
" * ZAP is an HTTP/HTTPS proxy for assessing web application security.\n" +
" *\n" +
" * Copyright 2016 the ZAP development team\n" +
" *\n" +
" * Licensed under the Apache License, Version 2.0 (the \"License\");\n" +
" * you may not use this file except in compliance with the License.\n" +
" * You may obtain a copy of the License at\n" +
" *\n" +
" * http://www.apache.org/licenses/LICENSE-2.0\n" +
" *\n" +
" * Unless required by applicable law or agreed to in writing, software\n" +
" * distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
" * See the License for the specific language governing permissions and\n" +
" * limitations under the License.\n" +
" */\n" +
"\n\n";
/**
* Map any names which are reserved in java to something legal
*/
private static final Map<String, String> nameMap;
static {
Map<String, String> initMap = new HashMap<>();
initMap.put("break", "brk");
initMap.put("continue", "cont");
nameMap = Collections.unmodifiableMap(initMap);
}
public JavaAPIGenerator() {
super(DEFAULT_OUTPUT_DIR);
}
public JavaAPIGenerator(String path, boolean optional) {
super(path, optional);
}
/**
* Generates the API client files of the given API implementors.
*
* @param implementors the implementors
* @throws IOException if an error occurred while generating the APIs.
* @deprecated (2.6.0) Use {@link #generateAPIFiles(List)} instead.
*/
@Deprecated
public void generateJavaFiles(List<ApiImplementor> implementors) throws IOException {
generateAPIFiles(implementors);
}
private void generateJavaElement(ApiElement element, String component,
String type, Writer out) throws IOException {
boolean hasParams = false;
// Add description if defined
String descTag = element.getDescriptionTag();
if (descTag == null) {
// This is the default, but it can be overriden by the getDescriptionTag method if required
descTag = component + ".api." + type + "." + element.getName();
}
try {
String desc = getMessages().getString(descTag);
out.write("\t/**\n");
out.write("\t * " + desc + "\n");
if (isOptional()) {
out.write("\t * <p>\n");
out.write("\t * " + OPTIONAL_MESSAGE + "\n");
}
if (element.isDeprecated()) {
out.write("\t * @deprecated");
String deprecationDesc = element.getDeprecatedDescription();
if (deprecationDesc != null && !deprecationDesc.isEmpty()) {
out.write(" " + deprecationDesc);
}
out.write("\n");
}
out.write("\t */\n");
} catch (Exception e) {
// Might not be set, so just print out the ones that are missing
System.out.println("No i18n for: " + descTag);
if (isOptional()) {
out.write("\t/**\n");
out.write("\t * " + OPTIONAL_MESSAGE + "\n");
out.write("\t */\n");
}
}
if (element.isDeprecated()) {
out.write("\t@Deprecated\n");
}
if (type.equals(OTHER_ENDPOINT)) {
out.write("\tpublic byte[] " + createMethodName(element.getName()) + "(");
} else {
out.write("\tpublic ApiResponse " + createMethodName(element.getName()) + "(");
}
if (element.getMandatoryParamNames() != null) {
for (String param : element.getMandatoryParamNames()) {
if (! hasParams) {
hasParams = true;
} else {
out.write(", ");
}
if (param.toLowerCase().equals("boolean")) {
out.write("boolean bool");
} else if (param.toLowerCase().equals("integer")) {
out.write("int i");
} else {
out.write("String ");
out.write(param.toLowerCase());
}
}
}
if (element.getOptionalParamNames() != null) {
for (String param : element.getOptionalParamNames()) {
if (! hasParams) {
hasParams = true;
} else {
out.write(", ");
}
if (param.toLowerCase().equals("boolean")) {
out.write("boolean bool");
} else if (param.toLowerCase().equals("integer")) {
out.write("int i");
} else {
out.write("String ");
out.write(param.toLowerCase());
}
}
}
out.write(") throws ClientApiException {\n");
if (hasParams) {
out.write("\t\tMap<String, String> map = new HashMap<>();\n");
if (element.getMandatoryParamNames() != null) {
for (String param : element.getMandatoryParamNames()) {
out.write("\t\tmap.put(\"" + param + "\", ");
if (param.toLowerCase().equals("boolean")) {
out.write("Boolean.toString(bool)");
} else if (param.toLowerCase().equals("integer")) {
out.write("Integer.toString(i)");
} else {
out.write(param.toLowerCase());
}
out.write(");\n");
}
}
if (element.getOptionalParamNames() != null) {
for (String param : element.getOptionalParamNames()) {
out.write("\t\tif (");
out.write(param.toLowerCase());
out.write(" != null) {\n");
out.write("\t\t\tmap.put(\"" + param + "\", ");
if (param.toLowerCase().equals("boolean")) {
out.write("Boolean.toString(bool)");
} else if (param.toLowerCase().equals("integer")) {
out.write("Integer.toString(i)");
} else {
out.write(param.toLowerCase());
}
out.write(");\n");
out.write("\t\t}\n");
}
}
}
out.write("\t\treturn api.callApi");
if (type.equals(OTHER_ENDPOINT)) {
out.write("Other");
}
out.write("(\"" + component + "\", \"" + type + "\", \"" + element.getName() + "\"");
if (hasParams) {
out.write(", map);\n");
} else {
out.write(", null);\n");
}
out.write("\t}\n\n");
}
private static String createMethodName(String name) {
if (nameMap.containsKey(name)) {
name = nameMap.get(name);
}
return removeAllFullStopCharacters(name);
}
private static String removeAllFullStopCharacters(String string) {
return string.replaceAll("\\.", "");
}
@Override
protected void generateAPIFiles(ApiImplementor imp) throws IOException {
String className = imp.getPrefix().substring(0, 1).toUpperCase() + imp.getPrefix().substring(1);
Path file = getDirectory().resolve(className + ".java");
System.out.println("Generating " + file.toAbsolutePath());
try (BufferedWriter out = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
out.write(HEADER);
out.write("package org.zaproxy.clientapi.gen;\n\n");
out.write("import java.util.HashMap;\n");
out.write("import java.util.Map;\n");
out.write("import org.zaproxy.clientapi.core.ApiResponse;\n");
out.write("import org.zaproxy.clientapi.core.ClientApi;\n");
out.write("import org.zaproxy.clientapi.core.ClientApiException;\n");
out.write("\n");
out.write("\n");
out.write("/**\n");
out.write(" * This file was automatically generated.\n");
out.write(" */\n");
out.write("@SuppressWarnings(\"javadoc\")\n");
out.write("public class " + className);
boolean extendsClass = false;
if (Files.exists(file.resolveSibling(Paths.get("deprecated", className + "Deprecated.java")))) {
out.write(" extends org.zaproxy.clientapi.gen.deprecated." + className + "Deprecated");
extendsClass = true;
}
out.write(" {\n\n");
out.write("\tprivate final ClientApi api;\n\n");
out.write("\tpublic " + className + "(ClientApi api) {\n");
if (extendsClass) {
out.write("\t\tsuper(api);\n");
}
out.write("\t\tthis.api = api;\n");
out.write("\t}\n\n");
for (ApiElement view : imp.getApiViews()) {
this.generateJavaElement(view, imp.getPrefix(), VIEW_ENDPOINT, out);
}
for (ApiElement action : imp.getApiActions()) {
this.generateJavaElement(action, imp.getPrefix(), ACTION_ENDPOINT, out);
}
for (ApiElement other : imp.getApiOthers()) {
this.generateJavaElement(other, imp.getPrefix(), OTHER_ENDPOINT, out);
}
out.write("}\n");
}
}
public static void main(String[] args) throws Exception {
// Command for generating a java version of the ZAP API
if (!Files.exists(Paths.get(DEFAULT_OUTPUT_DIR))) {
System.err.println("The directory does not exist: " + Paths.get(DEFAULT_OUTPUT_DIR).toAbsolutePath());
System.exit(1);
}
JavaAPIGenerator wapi = new JavaAPIGenerator();
wapi.generateCoreAPIFiles();
}
}