package org.develnext.jphp.genapi.template; import org.develnext.jphp.genapi.description.*; import org.develnext.jphp.genapi.parameter.BaseParameter; import org.develnext.jphp.genapi.parameter.MethodReturnParameter; import php.runtime.common.StringUtils; import java.io.*; import java.util.*; public class SphinxTemplate extends BaseTemplate { protected Map<String, List<ClassDescription>> classes = new LinkedHashMap<String, List<ClassDescription>>(); public SphinxTemplate(String language, String languageName) { super(language, languageName); } protected void echoType(String type) { sb.append(type.replaceAll("\\\\", "\\\\\\\\")); } protected void echoTypes(String... types) { int i = 0; if (types != null) { sb.append(":doc:`"); for(String type : types) { String ref = type; if (ref.endsWith("[]")) ref = ref.substring(0, ref.length() - 2); if (i != 0) sb.append(">`, :doc:`"); echoType(type); if (BaseParameter.isNotClass(ref)) ref = ".types/" + ref; sb.append(" </api_"+ language +"/").append(ref.replace('\\', '/')); i++; } sb.append(">`"); } } @Override protected void print(ClassDescription description) { sb.append(".. php:class:: "); echoType(description.getName()); String extend = description.getExtends(); String[] implement = description.getImplements(); sb.append("\n"); if (extend != null || implement != null || description.isAbstract() || description.isFinal()) { sb.append("\n"); if (description.isAbstract()) { sb.append(" **abstract** class\n\n"); } if (description.isFinal()) { sb.append(" **final** class\n\n"); } if (description.isInterface()) { sb.append(" **interface**\n\n"); } if (description.isTrait()) { sb.append(" **trait**\n\n"); } if (extend != null) { sb.append(" **extends**: "); echoTypes(extend); sb.append("\n\n"); } if (implement != null) { if (description.isInterface()) { sb.append(" **extends**: "); } else { sb.append(" **implements**: "); } echoTypes(implement); sb.append("\n\n"); } if (!description.getChildClasses().isEmpty()) { sb.append("**Children**").append("\n\n----------------------\n\n"); List<ClassDescription> tmp = new ArrayList<ClassDescription>(description.getChildClasses()); Collections.sort(tmp, new Comparator<ClassDescription>() { protected int getScore(ClassDescription e) { int i = 0; if (e.isAbstract()) i += 1000; if (e.isFinal()) i += 500; return i; } @Override public int compare(ClassDescription o1, ClassDescription o2) { int sc1 = getScore(o1); int sc2 = getScore(o2); if (sc1 == sc2) { return o1.getName().compareTo(o2.getName()); } else { return sc1 > sc2 ? -1 : 1; } } }); for(ClassDescription e : tmp) { sb.append(" * "); if (e.isAbstract()) sb.append("**abstract** "); if (e.isFinal()) sb.append("**final** "); if (e.isInterface()) sb.append("**interface** "); else sb.append("**class** "); echoTypes(e.getName()); sb.append("\n"); } } } if (description.getDescription() != null) { sb.append("\n "); sb.append(addTabToDescription(description.getDescription(), 1)); sb.append("\n"); } sb.append("\n"); } @Override protected void onBeforeClass(ClassDescription description) { String[] sections = StringUtils.split(description.getName(), "\\"); String namespace = sections.length == 1 ? "" : StringUtils.join(Arrays.copyOf(sections, sections.length - 1), "\\"); List<ClassDescription> list = classes.get(namespace); if (list == null) { classes.put(namespace, list = new ArrayList<ClassDescription>()); } list.add(description); echoType(description.getShortName()); sb.append("\n") .append(StringUtils.repeat('-', description.getName().length())) .append("\n\n"); String descFile = "/api_" + language + ".desc/" + description.getName().replace('\\', '/') + ".header.rst"; sb.append(".. include:: ").append(descFile).append("\n\n"); } @Override protected void onAfterClassBody(ClassDescription desc) { String descFile = "/api_" + language + ".desc/" + desc.getName().replace('\\', '/') + ".footer.rst"; sb.append("\n\n.. include:: ").append(descFile).append("\n\n"); } @Override protected void print(MethodDescription description) { print((FunctionDescription)description); } protected String addTabToDescription(String description, int tabCount) { description = getDescription(description, language); StringBuilder builder = new StringBuilder(); BufferedReader reader = new BufferedReader(new StringReader(description)); String line; int i = 0; try { while ((line = reader.readLine()) != null){ if (i != 0){ line = StringUtils.repeat(' ', tabCount) + line; builder.append("\n"); } builder.append(line); i++; } } catch (IOException e) { throw new RuntimeException(e); } return builder.toString(); } @Override protected void onBeforeMethods(ClassDescription desc) { sb.append("\n\n").append("**Methods**").append("\n\n----------\n\n"); } @Override protected void onBeforeProperties(ClassDescription desc) { sb.append("\n\n").append("**Properties**").append("\n\n----------\n\n"); } @Override protected void onBeforeConstants(ClassDescription desc) { sb.append("\n\n").append("**Constants**").append("\n\n----------\n\n"); } @Override protected void print(ConstantDescription description) { sb.append(" .. php:const:: ").append(description.getName()).append("\n\n"); if (description.getDescription() != null && !description.getDescription().isEmpty()) { sb.append(" ") .append(addTabToDescription(description.getDescription().trim(), 2)) .append("\n\n"); } } @Override protected void print(PropertyDescription desc) { sb.append(" .. php:attr:: ").append(desc.getName()).append("\n"); if (desc.getTypes() != null && desc.getTypes().length > 0) { sb.append("\n"); sb.append(" "); echoTypes(desc.getTypes()); sb.append("\n"); } sb.append("\n"); boolean add = false; if (desc.isStatic()) { sb.append(" **static**\n\n"); add = true; } if (desc.isPrivate()) { sb.append(" **private**\n\n"); add = true; } if (desc.isProtected()) { sb.append(" **protected**\n\n"); add = true; } if (desc.isReadonly()) { sb.append(" **read-only**\n\n"); add = true; } if (add) sb.append("\n"); if (desc.getDescription() != null && !desc.getDescription().isEmpty()) { sb.append(" ") .append(addTabToDescription(desc.getDescription().trim(), 2)) .append("\n\n"); } } @Override protected void print(FunctionDescription description) { if (description instanceof MethodDescription && ((MethodDescription) description).isStatic()) { sb.append(" .. php:staticmethod:: "); } else { sb.append(" .. php:method:: "); } sb.append(description.getName()).append("("); int i = 0; Collection<ArgumentDescription> args = description.getArguments(); for (ArgumentDescription arg : args) { if (i != 0) sb.append(", "); sb.append("$").append(arg.getName()); if (arg.getValue() != null) { sb.append(" = ").append(arg.getValue()); } i++; } sb.append(")\n\n"); if (description instanceof MethodDescription) { MethodDescription meth = (MethodDescription)description; boolean add = false; if (meth.isFinal()) { sb.append(" **final**\n\n"); add = true; } if (meth.isAbstract()) { sb.append(" **abstract**\n\n"); add = true; } if (meth.isPrivate()) { sb.append(" **private**\n\n"); add = true; } if (meth.isProtected()) { sb.append(" **protected**\n\n"); add = true; } if (add) sb.append("\n"); } if (description.getDescription() != null && !description.getDescription().isEmpty()) { sb.append(" ") .append(addTabToDescription(description.getDescription().trim(), 2)) .append("\n\n"); } if (description.getThrowsParameters() != null) { for(MethodReturnParameter e : description.getThrowsParameters()) { sb.append(" **throws** "); echoTypes(e.getTypes()); if (e.getDescription() != null && !e.getDescription().trim().isEmpty()) { sb.append(" "); sb.append(addTabToDescription(e.getDescription().trim(), 2)); } sb.append("\n\n"); } } } @Override protected void print(ArgumentDescription description) { sb.append(" :param "); sb.append("$").append(description.getName()).append(": "); if (description.getTypes() != null) { echoTypes(description.getTypes()); sb.append(" "); } if (description.getDescription() != null && !description.getDescription().trim().isEmpty()) { sb.append(" - "); sb.append(addTabToDescription(description.getDescription(), 2)); } sb.append("\n"); } @Override protected void onAfterMethod(MethodDescription desc) { onAfterFunction(desc); } @Override protected void onAfterFunction(FunctionDescription desc) { if (desc.getReturnTypes() != null || (desc.getReturnDescription() != null && !desc.getReturnDescription().isEmpty())) { sb.append(" :returns: "); if (desc.getReturnTypes() != null) { echoTypes(desc.getReturnTypes()); sb.append(" "); } if (desc.getReturnDescription() != null) { sb.append(addTabToDescription(desc.getReturnDescription().trim(), 2)); } sb.append("\n"); } sb.append("\n"); } @Override public void onEnd(File targetDirectory) { onEnd(targetDirectory, null); } protected List<String> onEnd(File targetDirectory, String namespace) { StringBuilder sb = new StringBuilder(); String sectionName; if (namespace != null) { String[] tmp = StringUtils.split(namespace, '\\'); sectionName = tmp[tmp.length - 1]; } else { sectionName = "API (" + languageName + ")"; } sb.append(sectionName) .append("\n") .append(StringUtils.repeat('-', 30)).append("\n\n"); sb.append("\n.. include:: /api_") .append(language) .append(".desc/"); if (namespace != null) { sb.append(namespace.replace('\\', '/')).append("/"); } sb.append("index.header.rst\n\n"); sb.append(".. toctree::\n" + " :maxdepth: 3\n\n"); File[] files = targetDirectory.listFiles(); List<String> ctree = new ArrayList<String>(); if (files != null) { for(File dir : files) { if (dir.isFile() && dir.getName().endsWith(".rst")) { if (!dir.getName().equals("index.rst")) { ctree.add(dir.getName()); } } else if (dir.isDirectory() && !dir.getName().startsWith(".")) { ctree.add(dir.getName() + "/index"); } } for(File dir : files) { if (dir.isDirectory() && !dir.getName().startsWith(".")) { List<String> tmp = onEnd(dir, namespace == null ? dir.getName() : namespace + "\\" + dir.getName()); if (ctree.isEmpty() || ctree.size() == 1) { ctree.clear(); for(String e : tmp) { ctree.add(dir.getName() + "/" + e); } } } } } for(String e : ctree) { sb.append(" ").append(e).append("\n"); } sb.append("\n.. include:: /api_") .append(language) .append(".desc/"); if (namespace != null) { sb.append(namespace.replace('\\', '/')).append("/"); } sb.append("index.footer.rst\n\n"); File file = new File(targetDirectory, "/index.rst"); try { FileWriter fileWriter = new FileWriter(file, false); fileWriter.write(sb.toString()); fileWriter.close(); } catch (IOException e) { throw new RuntimeException(e); } return ctree; } }