public static byte[] fetchContent(
      Context context, String language, String page, boolean readOnly, int templateType) {
    Class[] scriptingClasses = {JavaScriptEngine.class, SchemeEngine.class};

    language = language.toLowerCase();

    ArrayList<String> languages = new ArrayList<>();
    languages.add(BootstrapSiteExporter.LANGUAGE_ALL.toLowerCase());

    if (languages.contains(language) == false) languages.add(language.toLowerCase());

    if (page == null || page.trim().length() == 0) {
      try {
        JSONArray declaredMethods = BootstrapSiteExporter.methodsForLanguage(context, language);

        AssetManager am = context.getAssets();

        String template = "embedded_website/docs/scripting_template.html";

        if (readOnly && templateType == BootstrapSiteExporter.TEMPLATE_TYPE_BOOTSTRAP)
          template = "embedded_website/docs/scripting_template_readonly.html";
        else if (templateType == BootstrapSiteExporter.TEMPLATE_TYPE_JEKYLL)
          template = "embedded_website/docs/scripting_template_jekyll.html";

        InputStream in = am.open(template);

        // http://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string
        Scanner s = new Scanner(in).useDelimiter("\\A");

        String content = "";

        if (s.hasNext()) content = s.next();

        content = content.replace("{{ METHOD_DEFINITIONS }}", declaredMethods.toString(2));
        content = content.replace("{{ LANGUAGE }}", language.toLowerCase());

        return content.getBytes(Charset.forName("UTF-8"));
      } catch (IOException | JSONException e) {
        e.printStackTrace();
      }

      return "404 ERROR".getBytes(Charset.forName("UTF-8"));
    } else {
      if (page.startsWith("all_")) language = "all";

      try {
        AssetManager am = context.getAssets();

        String template = "embedded_website/docs/method_template.html";

        if (readOnly && templateType == BootstrapSiteExporter.TEMPLATE_TYPE_BOOTSTRAP)
          template = "embedded_website/docs/method_template_readonly.html";
        else if (templateType == BootstrapSiteExporter.TEMPLATE_TYPE_JEKYLL)
          template = "embedded_website/docs/method_template_jekyll.html";

        InputStream in = am.open(template);

        // http://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string
        Scanner s = new Scanner(in).useDelimiter("\\A");

        String content = "";

        if (s.hasNext()) content = s.next();

        s.close();

        in = am.open("embedded_website/docs/" + language.toLowerCase() + "/" + page);

        s = new Scanner(in).useDelimiter("\\A");

        String pageContent = "";

        if (s.hasNext()) pageContent = s.next();

        s.close();

        for (Class classObj : scriptingClasses) {
          Method[] methods = classObj.getMethods();

          for (Method method : methods) {
            Annotation[] annotations = method.getDeclaredAnnotations();

            for (Annotation annotation : annotations) {
              if (annotation instanceof ScriptingEngineMethod) {
                ScriptingEngineMethod scriptAnnotation = (ScriptingEngineMethod) annotation;

                if (page.equals(scriptAnnotation.assetPath())) {
                  StringBuilder args = new StringBuilder();

                  for (String argument : ((ScriptingEngineMethod) annotation).arguments()) {
                    if (args.length() > 0) args.append(", ");

                    args.append(argument);
                  }

                  content =
                      content.replace(
                          "{{ METHOD_NAME }}", method.getName() + "(" + args.toString() + ")");
                  content =
                      content.replace(
                          "{{ LANGUAGE }}",
                          ((ScriptingEngineMethod) annotation).language().toLowerCase());
                  content = content.replace("{{ PAGE }}", page);
                }
              }
            }
          }
        }

        content = content.replace("{{ METHOD_DOCUMENTATION }}", pageContent);

        return content.getBytes(Charset.forName("UTF-8"));
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    return "404 ERROR".getBytes(Charset.forName("UTF-8"));
  }
  private static JSONArray methodsForLanguage(Context context, String language) {
    Class[] scriptingClasses = {JavaScriptEngine.class, SchemeEngine.class};

    ArrayList<String> languages = new ArrayList<>();
    languages.add(BootstrapSiteExporter.LANGUAGE_ALL.toLowerCase());

    if (languages.contains(language) == false) languages.add(language.toLowerCase());

    JSONArray declaredMethods = new JSONArray();
    HashSet<String> included = new HashSet<>();

    for (Class classObj : scriptingClasses) {
      Method[] methods = classObj.getMethods();

      for (Method method : methods) {
        Annotation[] annotations = method.getDeclaredAnnotations();

        for (Annotation annotation : annotations) {
          if (annotation instanceof ScriptingEngineMethod) {
            ScriptingEngineMethod scriptAnnotation = (ScriptingEngineMethod) annotation;

            String category = context.getString(scriptAnnotation.category());
            String assetPath = scriptAnnotation.assetPath();
            String scriptLanguage = scriptAnnotation.language();
            String methodName = method.getName();

            if (languages.contains(scriptLanguage.toLowerCase())
                && included.contains(method.toGenericString()) == false) {
              StringBuilder args = new StringBuilder();

              for (String argument : ((ScriptingEngineMethod) annotation).arguments()) {
                if (args.length() > 0) args.append(", ");

                args.append(argument);
              }

              JSONObject methodDef = new JSONObject();

              try {
                methodDef.put(
                    BootstrapSiteExporter.METHOD_NAME, methodName + "(" + args.toString() + ")");
                methodDef.put(BootstrapSiteExporter.METHOD_FULL_NAME, method.toGenericString());
                methodDef.put(BootstrapSiteExporter.METHOD_LANGUAGE, scriptLanguage);
                methodDef.put(BootstrapSiteExporter.METHOD_CATEGORY, category);

                if (assetPath.trim().length() > 0)
                  methodDef.put(BootstrapSiteExporter.METHOD_ASSET_PATH, assetPath);
              } catch (JSONException e) {
                e.printStackTrace();
              }

              declaredMethods.put(methodDef);

              included.add(method.toGenericString());
            }
          }
        }
      }
    }

    return declaredMethods;
  }