| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.netbeans.modules.javascript2.nodejs.editor; |
| |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.io.OutputStreamWriter; |
| import java.io.Reader; |
| import java.io.Writer; |
| import java.net.MalformedURLException; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.nio.charset.Charset; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.WeakHashMap; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.ChangeListener; |
| import org.json.simple.JSONArray; |
| import org.json.simple.JSONObject; |
| import org.json.simple.JSONValue; |
| import org.netbeans.api.progress.ProgressHandle; |
| import org.netbeans.api.project.FileOwnerQuery; |
| import org.netbeans.api.project.Project; |
| import org.netbeans.modules.csl.api.Documentation; |
| import org.netbeans.modules.csl.api.OffsetRange; |
| import org.netbeans.modules.javascript2.types.api.DeclarationScope; |
| import org.netbeans.modules.javascript2.model.api.JsObject; |
| import org.netbeans.modules.javascript2.model.spi.ModelElementFactory; |
| import org.netbeans.modules.javascript2.nodejs.spi.NodeJsSupport; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.modules.Places; |
| import org.openide.util.Exceptions; |
| import org.openide.util.NbBundle; |
| import org.openide.util.RequestProcessor; |
| import org.openide.util.WeakListeners; |
| |
| /** |
| * |
| * @author Petr Pisl |
| */ |
| @NbBundle.Messages({ |
| "doc.building=Loading NodeJS Documentation", |
| "# {0} - the documentation URL", |
| "doc.cannotGet=Cannot load NodeJS documentation from \"{0}\".", |
| "doc.notFound=Documentation not found." |
| }) |
| public class NodeJsDataProvider { |
| |
| private static final Logger LOG = Logger.getLogger(NodeJsDataProvider.class.getSimpleName()); |
| |
| private static RequestProcessor RP = new RequestProcessor(NodeJsDataProvider.class); |
| private boolean loadingStarted; |
| private ProgressHandle progress; |
| |
| private static final String API_ALL_HTML_FILE = "all.html"; |
| private static final String CACHE_FOLDER_NAME = "nodejs-doc"; //NOI18N |
| private static final String API_ALL_JSON_FILE = "all.json"; //NOI18N |
| |
| protected static final String BACKUP_API_FILE = new StringBuilder().append(CACHE_FOLDER_NAME).append("/latest/") //NOI18N |
| .append(API_ALL_JSON_FILE).toString(); |
| private static final int URL_CONNECTION_TIMEOUT = 1000; //ms |
| private static final int URL_READ_TIMEOUT = URL_CONNECTION_TIMEOUT * 3; //ms |
| |
| private static final String AP_STRING = "'"; //NOI18N |
| private static final String REQUIRE_STRING = "= require(" + AP_STRING; //NOI18N |
| |
| private static final String JS_EXT = "js"; //NOI18N |
| |
| // name of the json fields in api file |
| private static final String MODULES = "modules"; //NOI18N |
| private static final String NAME = "name"; //NOI18N |
| private static final String DESCRIPTION = "desc"; //NOI18N |
| private static final String GLOBALS = "globals"; //NOI18N |
| private static final String VARS = "vars"; //NOI18N |
| private static final String PARAMS = "params"; //NOI18N |
| private static final String METHODS = "methods"; //NOI18N |
| private static final String PROPERTIES = "properties"; //NOI18N |
| private static final String CLASSES = "classes"; //NOI18N |
| private static final String EVENTS = "events"; //NOI18N |
| |
| // name of the json fields in package.json |
| private static final String MODULE_VERSION = "version"; //NOI18N |
| private static final String MODULE_DESCRIPTION = "description"; //NOI18N |
| |
| private static final WeakHashMap<Project, NodeJsDataProvider> cache = new WeakHashMap<>(); |
| private static NodeJsDataProvider noProjectInstance = null; |
| |
| private static String docApiFilePath = BACKUP_API_FILE; |
| private FileObject docFolder; |
| private String docUrl = "https://nodejs.org/api/"; //NOI18N |
| private boolean isSupportEnabled; |
| |
| private ProjectSupportChangeListener listener; |
| |
| /** |
| * Caching the apifile from sources folder. |
| */ |
| private File apiFile = null; |
| |
| private NodeJsDataProvider(Project project) { |
| this.loadingStarted = false; |
| this.isSupportEnabled = project == null; |
| this.docFolder = null; |
| if (project != null) { |
| NodeJsSupport support = null; |
| support = project.getLookup().lookup(NodeJsSupport.class); |
| if (support != null) { |
| listener = new ProjectSupportChangeListener(project); |
| support.addChangeListener(WeakListeners.change(listener, support)); |
| this.isSupportEnabled = support.isSupportEnabled(); |
| this.docFolder = support.getDocumentationFolder(); |
| if (support.getDocumentationUrl() != null) { |
| this.docUrl = support.getDocumentationUrl(); |
| } |
| if (support.getVersion() != null) { |
| docApiFilePath = new StringBuilder().append(CACHE_FOLDER_NAME).append(File.separator) |
| .append(support.getVersion().toString()).append(File.separator).append(API_ALL_JSON_FILE).toString(); |
| } |
| } |
| } |
| } |
| |
| public static synchronized NodeJsDataProvider getDefault(FileObject fo) { |
| assert fo != null; |
| Project project = FileOwnerQuery.getOwner(fo); |
| if (project == null) { |
| if (noProjectInstance == null) { |
| noProjectInstance = new NodeJsDataProvider(null); |
| } |
| return noProjectInstance; |
| } |
| NodeJsDataProvider instance = cache.get(project); |
| if (instance == null) { |
| instance = new NodeJsDataProvider(project); |
| cache.put(project, instance); |
| } |
| |
| return instance; |
| } |
| |
| public boolean isSupportEnabled() { |
| return isSupportEnabled; |
| } |
| |
| /** |
| * |
| * @return URL or null if it's not available. |
| */ |
| private URL getDocumentationURL() { |
| URL result = null; |
| try { |
| result = new URL(docUrl); |
| } catch (MalformedURLException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| return result; |
| } |
| |
| /** |
| * |
| * @return folder with the sources of the runtime modules or null |
| */ |
| public FileObject getFolderWithRuntimeSources () { |
| if (docFolder != null) { |
| return docFolder.getFileObject("../lib"); //NOI18N |
| } |
| return null; |
| } |
| |
| /** |
| * |
| * @return list of names of runtime modules. These names are obtained as names of |
| * files from ${docfolder}/../lib or from the documentation if the doc folder doesn't |
| * exist |
| */ |
| public Collection<String> getRuntimeModules() { |
| HashSet<String> modules = new HashSet<String>(); |
| if (docFolder != null) { |
| FileObject libFolder = getFolderWithRuntimeSources(); |
| if (libFolder != null) { |
| FileObject[] children = libFolder.getChildren(); |
| for (int i = 0; i < children.length; i++) { |
| FileObject module = children[i]; |
| if (!module.isFolder() && JS_EXT.equals(module.getExt()) && module.getName().charAt(0) != '_' ) { |
| modules.add(module.getName()); |
| } |
| |
| } |
| if (!modules.isEmpty()) { |
| return modules; |
| } |
| } |
| } |
| String content = getContentApiFile(); |
| if (content != null) { |
| int index = 0; |
| int lenghtOfRequire = REQUIRE_STRING.length(); |
| index = content.indexOf(REQUIRE_STRING, index); |
| while (index != -1) { |
| index += lenghtOfRequire; |
| if (content.charAt(index) != '.') { |
| int end = content.indexOf(AP_STRING, index); |
| if (end > -1) { |
| String module = content.substring(index, end); |
| modules.add(module); |
| } |
| } |
| index = content.indexOf(REQUIRE_STRING, index); |
| } |
| } |
| return modules; |
| } |
| |
| /** |
| * |
| * @return collection of local modules, that are obtained from the first node_modules folder |
| */ |
| public Collection<FileObject> getLocalModules(FileObject forFile) { |
| HashSet<FileObject> modules = new HashSet<FileObject>(); |
| Project project = FileOwnerQuery.getOwner(forFile); |
| FileObject nodeModulesFolder = null; |
| FileObject parent = forFile.getParent(); |
| if (project != null) { |
| FileObject projectDirectory = project.getProjectDirectory(); |
| String pathToProject = projectDirectory.getPath(); |
| while (parent.getPath().startsWith(pathToProject) && nodeModulesFolder == null) { |
| nodeModulesFolder = parent.getFileObject(NodeJsUtils.NODE_MODULES_NAME); |
| parent = parent.getParent(); |
| } |
| } |
| if (nodeModulesFolder != null) { |
| Enumeration<? extends FileObject> moduleFolders = nodeModulesFolder.getFolders(false); |
| while (moduleFolders.hasMoreElements()) { |
| FileObject moduleFolder = moduleFolders.nextElement(); |
| if (moduleFolder.getFileObject(NodeJsUtils.PACKAGE_NAME, NodeJsUtils.JSON_EXT) != null) { |
| modules.add(moduleFolder); |
| } |
| } |
| } |
| return modules; |
| } |
| |
| public Map<String, Collection<String>> getAllEvents() { |
| HashMap<String, Collection<String>> result = new HashMap<>(); |
| String content = getContentApiFile(); |
| if (content != null && !content.isEmpty()) { |
| JSONObject root = (JSONObject) JSONValue.parse(content); |
| JSONArray globals = getJSONArrayProperty(root, GLOBALS); |
| if (globals != null) { |
| for (Object jsonValue : globals) { |
| if (jsonValue instanceof JSONObject) { |
| getNameOfEventsRecursively((JSONObject)jsonValue, result); |
| } |
| } |
| } |
| JSONArray modules = getJSONArrayProperty(root, MODULES); |
| if (modules != null) { |
| for (Object jsonValue : modules) { |
| if (jsonValue instanceof JSONObject) { |
| getNameOfEventsRecursively((JSONObject)jsonValue, result); |
| } |
| } |
| } |
| JSONArray vars = getJSONArrayProperty(root, VARS); |
| if (vars != null) { |
| for (Object jsonValue : vars) { |
| if (jsonValue instanceof JSONObject) { |
| getNameOfEventsRecursively((JSONObject)jsonValue, result); |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| private void getNameOfEventsRecursively(JSONObject object, Map<String, Collection<String>> result) { |
| JSONArray events = getJSONArrayProperty(object, EVENTS); |
| if (events != null) { |
| String objectName = getJSONStringProperty(object, NAME); |
| StringBuilder docHeader = new StringBuilder(); |
| docHeader.append("<h2>").append(objectName).append("</h2>"); //NOI18N |
| for (Object jsonValue : events) { |
| if (jsonValue instanceof JSONObject) { |
| JSONObject event = (JSONObject) jsonValue; |
| String name = getJSONStringProperty(event, NAME); |
| Collection<String> documentations = result.get(name); |
| if (documentations == null) { |
| documentations = new ArrayList<String>(); |
| result.put(name, documentations); |
| } |
| String documentation = getJSONStringProperty(event, DESCRIPTION); |
| if (documentation != null && !documentation.isEmpty()) { |
| documentations.add(docHeader.toString() + documentation); |
| } |
| } |
| } |
| } |
| JSONArray classes = getJSONArrayProperty(object, CLASSES); |
| if (classes != null) { |
| for (Object jsonValue : classes) { |
| if (jsonValue instanceof JSONObject) { |
| getNameOfEventsRecursively((JSONObject)jsonValue, result); |
| } |
| } |
| } |
| } |
| |
| |
| public String getDocForModule(final String moduleName) { |
| Object jsonValue; |
| JSONArray modules = getModules(); |
| if (modules != null) { |
| for (int i = 0; i < modules.size(); i++) { |
| jsonValue = modules.get(i); |
| if (jsonValue != null && jsonValue instanceof JSONObject) { |
| JSONObject jsonModule = (JSONObject) jsonValue; |
| jsonValue = jsonModule.get(NAME); |
| if (jsonValue != null && jsonValue instanceof String && moduleName.equals(((String) jsonValue).toLowerCase())) { |
| jsonValue = jsonModule.get(DESCRIPTION); |
| if (jsonValue != null && jsonValue instanceof String) { |
| return (String) jsonValue; |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| @NbBundle.Messages({"NodeJsDataprovider.lbl.name=Name:", "NodeJsDataprovider.lbl.version=Version:"}) //NOI18N |
| public String getDocForLocalModule(final FileObject moduleFolder) { |
| FileObject packageFO = moduleFolder.getFileObject(NodeJsUtils.PACKAGE_NAME, NodeJsUtils.JSON_EXT); |
| if (packageFO != null) { |
| String content = null; |
| try { |
| content = getFileContent(FileUtil.toFile(packageFO)); |
| } catch (IOException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| if (content != null && !content.isEmpty()) { |
| JSONObject root = (JSONObject) JSONValue.parse(content); |
| if (root != null) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(Bundle.NodeJsDataprovider_lbl_name()).append(" <b>").append(getJSONStringProperty(root, NAME)).append("</b><br/>"); |
| sb.append(Bundle.NodeJsDataprovider_lbl_version()).append(" ").append(getJSONStringProperty(root, MODULE_VERSION)).append("<br/><br/>"); |
| sb.append(getJSONStringProperty(root, MODULE_DESCRIPTION)); |
| return sb.toString(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| public Collection<JsObject> getGlobalObjects(ModelElementFactory factory) { |
| String content = getContentApiFile(); |
| if (content != null && !content.isEmpty()) { |
| File apiFile = getCachedAPIFile(); |
| JsObject globalObject = factory.newGlobalObject(FileUtil.toFileObject(apiFile), (int) apiFile.length()); |
| JSONObject root = (JSONObject) JSONValue.parse(content); |
| if (root != null) { |
| JSONArray globals = getJSONArrayProperty(root, GLOBALS); |
| if (globals != null) { |
| for (Object jsonValue : globals) { |
| if (jsonValue instanceof JSONObject) { |
| JSONObject global = (JSONObject) jsonValue; |
| String name = getJSONStringProperty(global, NAME); |
| if (name != null) { |
| JsObject property = createProperty(factory, globalObject, global); |
| addProperties(factory, property, (DeclarationScope) globalObject, global); |
| addMethods(factory, property, (DeclarationScope) globalObject, global); |
| } |
| } |
| } |
| } |
| JSONArray vars = getJSONArrayProperty(root, VARS); |
| if (vars != null) { |
| for (Object jsonValue : vars) { |
| if (jsonValue instanceof JSONObject) { |
| JSONObject var = (JSONObject) jsonValue; |
| String name = getJSONStringProperty(var, NAME); |
| if (name != null) { |
| // if (REQUIRE_STRING.equals(name)) { |
| // |
| // } else { |
| // |
| // } |
| JsObject property = createProperty(factory, globalObject, var); |
| addProperties(factory, property, (DeclarationScope) globalObject, var); |
| addMethods(factory, property, (DeclarationScope) globalObject, var); |
| } |
| } |
| } |
| } |
| addMethods(factory, globalObject, (DeclarationScope) globalObject, root); |
| } |
| return Collections.singletonList(globalObject); |
| } |
| return Collections.emptyList(); |
| } |
| |
| private void addMethods(final ModelElementFactory factory, final JsObject toObject, final DeclarationScope scope, final JSONObject fromObject) { |
| JSONArray methods = getJSONArrayProperty(fromObject, METHODS); |
| if (methods != null) { |
| for (Object methodO : methods) { |
| if (methodO instanceof JSONObject) { |
| JSONObject method = (JSONObject) methodO; |
| String methodName = getJSONStringProperty(method, NAME); |
| JSONArray signatures = getJSONArrayProperty(method, "signatures"); |
| String doc = getJSONStringProperty(method, DESCRIPTION); |
| if (methodName != null && signatures != null) { |
| for (Object signature : signatures) { |
| JSONArray params = getJSONArrayProperty((JSONObject) signature, PARAMS); |
| List<String> paramNames = new ArrayList<String>(); |
| if (params != null && !params.isEmpty()) { |
| for (Object param : params) { |
| String paramName = getJSONStringProperty((JSONObject) param, NAME); |
| if (paramName != null) { |
| paramNames.add(paramName); |
| } |
| } |
| } |
| JsObject object = factory.newFunction(scope, toObject, methodName, paramNames, NodeJsUtils.NODEJS_NAME); |
| if(doc != null) { |
| object.setDocumentation(Documentation.create(doc, getDocumentationURL(methodName, paramNames))); |
| } |
| toObject.addProperty(object.getName(), object); |
| addProperties(factory, object, (DeclarationScope) object, method); |
| addMethods(factory, object, (DeclarationScope) object, method); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private JsObject createProperty(final ModelElementFactory factory, final JsObject parent, final JSONObject jsonObject) { |
| String propertyName = getJSONStringProperty(jsonObject, NAME); |
| if (propertyName != null) { |
| JsObject object = factory.newObject(parent, propertyName, OffsetRange.NONE, true, NodeJsUtils.NODEJS_NAME); |
| parent.addProperty(object.getName(), object); |
| String doc = getJSONStringProperty(jsonObject, DESCRIPTION); |
| if(doc != null) { |
| object.setDocumentation(Documentation.create(doc, getDocumentationURL(propertyName))); |
| } |
| return object; |
| } |
| return null; |
| } |
| |
| private void addProperties(final ModelElementFactory factory, final JsObject toObject, final DeclarationScope scope, final JSONObject fromObject) { |
| JSONArray properties = getJSONArrayProperty(fromObject, PROPERTIES); |
| if (properties != null) { |
| for (Object propertyO : properties) { |
| if (propertyO instanceof JSONObject) { |
| JSONObject property = (JSONObject) propertyO; |
| JsObject newProperty = createProperty(factory, toObject, property); |
| if (newProperty != null) { |
| addProperties(factory, newProperty, scope, property); |
| addMethods(factory, newProperty, scope, property); |
| } |
| } |
| } |
| } |
| } |
| |
| private String getJSONStringProperty(final JSONObject object, final String property) { |
| Object value = object.get(property); |
| if (value != null && value instanceof String) { |
| return (String) value; |
| } |
| return null; |
| } |
| |
| private JSONArray getJSONArrayProperty(final JSONObject object, final String property) { |
| Object value = object.get(property); |
| if (value != null && value instanceof JSONArray) { |
| return (JSONArray) value; |
| } |
| return null; |
| } |
| |
| private URL getDocumentationURL(String name) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(docUrl).append(API_ALL_HTML_FILE).append("#all_"); |
| String alteredName = name; |
| while (alteredName.charAt(0) == '_') { |
| alteredName = alteredName.substring(1); |
| } |
| sb.append(alteredName.toLowerCase()); |
| URL result = null; |
| try { |
| result = new URL(sb.toString()); |
| } catch (MalformedURLException ex) { |
| // Do nothing |
| } |
| return result; |
| } |
| |
| private URL getDocumentationURL(String name, Collection<String> params) { |
| URL result = getDocumentationURL(name); |
| if (result != null) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(result.toExternalForm()); |
| for (String param : params) { |
| sb.append('_').append(param); |
| } |
| result = null; |
| try { |
| result = new URL(sb.toString()); |
| } catch (MalformedURLException ex) { |
| // Do nothing |
| } |
| } |
| return result; |
| } |
| |
| private JSONArray getModules() { |
| String content = getContentApiFile(); |
| if (content != null && !content.isEmpty()) { |
| JSONObject root = (JSONObject) JSONValue.parse(content); |
| if (root != null) { |
| Object jsonValue = root.get(MODULES); |
| if (jsonValue != null && jsonValue instanceof JSONArray) { |
| return (JSONArray) jsonValue; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private void loadURL(URL url, Writer writer, Charset charset) throws IOException { |
| if (charset == null) { |
| charset = Charset.defaultCharset(); |
| } |
| URLConnection con = url.openConnection(); |
| con.setConnectTimeout(URL_CONNECTION_TIMEOUT); |
| con.setReadTimeout(URL_READ_TIMEOUT); |
| con.connect(); |
| try (Reader r = new InputStreamReader(new BufferedInputStream(con.getInputStream()), charset)) { |
| char[] buf = new char[2048]; |
| int read; |
| while ((read = r.read(buf)) != -1) { |
| writer.write(buf, 0, read); |
| } |
| } |
| } |
| |
| private String getFileContent(File file) throws IOException { |
| Reader r = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8); |
| StringBuilder sb = new StringBuilder(); |
| try { |
| char[] buf = new char[2048]; |
| int read; |
| while ((read = r.read(buf)) != -1) { |
| sb.append(buf, 0, read); |
| } |
| } finally { |
| r.close(); |
| } |
| return sb.toString(); |
| } |
| |
| private File getCachedAPIFile() { |
| if (apiFile != null) { |
| return apiFile; |
| } |
| if (docFolder != null) { |
| for (FileObject folder : Collections.list(docFolder.getFolders(false))) { |
| FileObject fo = folder.getFileObject(API_ALL_JSON_FILE); |
| if (fo != null) { |
| apiFile = FileUtil.toFile(fo); |
| return apiFile; |
| } |
| } |
| } |
| File cacheFile = Places.getCacheSubfile(docApiFilePath); |
| return cacheFile; |
| } |
| |
| private String getContentApiFile() { |
| String result = null; |
| try { |
| File cacheFile = getCachedAPIFile(); |
| if (!cacheFile.exists() && isSupportEnabled()) { |
| |
| //if any of the files is not loaded yet, start the loading process |
| if (!loadingStarted) { |
| startLoading(); |
| } |
| //load from web and cache locally |
| loadDoc(cacheFile); |
| stopLoading(); |
| |
| LOG.log(Level.FINE, "Loading doc finished."); //NOI18N |
| } |
| result = cacheFile.exists() ? getFileContent(cacheFile) : null; |
| } catch (URISyntaxException | IOException ex) { |
| stopLoading(); |
| LOG.log(Level.INFO, "Cannot load NodeJS documentation from \"{0}\".", new Object[]{getDocumentationURL()}); //NOI18N |
| LOG.log(Level.INFO, "", ex); |
| } |
| return result; |
| } |
| |
| private void startLoading() { |
| LOG.fine("start loading doc"); //NOI18N |
| loadingStarted = true; |
| progress = ProgressHandle.createHandle(Bundle.doc_building()); |
| progress.start(1); |
| } |
| |
| private void stopLoading() { |
| loadingStarted = false; |
| if (progress != null) { |
| progress.progress(1); |
| progress.finish(); |
| progress = null; |
| } |
| } |
| |
| private void loadDoc(File cacheFile) throws URISyntaxException, MalformedURLException, IOException { |
| LOG.fine("start loading doc"); //NOI18N |
| URL url = new URL(getDocumentationURL().toExternalForm() + API_ALL_JSON_FILE); |
| synchronized (cacheFile) { |
| String tmpFileName = cacheFile.getAbsolutePath() + ".tmp"; //NOI18N |
| File tmpFile = new File(tmpFileName); |
| try (Writer writer = new OutputStreamWriter(new FileOutputStream(tmpFile), StandardCharsets.UTF_8)) { |
| loadURL(url, writer, StandardCharsets.UTF_8); |
| writer.close(); |
| tmpFile.renameTo(cacheFile); |
| } finally { |
| if (tmpFile.exists()) { |
| tmpFile.delete(); |
| } |
| } |
| |
| } |
| } |
| |
| public String getDocumentationForGlobalObject(String nameObject) { |
| String content = getContentApiFile(); |
| if (content != null && !content.isEmpty()) { |
| File apiFile = getCachedAPIFile(); |
| JSONObject root = (JSONObject) JSONValue.parse(content); |
| if (root != null) { |
| JSONArray globals = getJSONArrayProperty(root, GLOBALS); |
| if (globals != null) { |
| for (Object jsonValue : globals) { |
| if (jsonValue instanceof JSONObject) { |
| JSONObject global = (JSONObject) jsonValue; |
| String name = getJSONStringProperty(global, NAME); |
| if (name != null && name.equals(nameObject)) { |
| String doc = getJSONStringProperty(global, DESCRIPTION); |
| return doc; |
| } |
| } |
| } |
| } |
| JSONArray vars = getJSONArrayProperty(root, VARS); |
| if (vars != null) { |
| for (Object jsonValue : vars) { |
| if (jsonValue instanceof JSONObject) { |
| JSONObject var = (JSONObject) jsonValue; |
| String name = getJSONStringProperty(var, NAME); |
| if (name != null && name.equals(nameObject)) { |
| String doc = getJSONStringProperty(var, DESCRIPTION); |
| return doc; |
| } |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| /** |
| * |
| * @param fqn fully qualified name of the type. |
| * @return |
| */ |
| String getDocumentation(String fqn) { |
| String moduleName = fqn.startsWith(NodeJsUtils.FAKE_OBJECT_NAME_PREFIX) |
| ? fqn.substring(NodeJsUtils.FAKE_OBJECT_NAME_PREFIX.length()) : fqn; |
| String[] parts = moduleName.split("\\."); |
| if (parts.length > 2 && parts[0].equals(parts[1])) { |
| // remove the first part of the fqn, because it's artificially added |
| // to the model to keep the global context clean |
| parts = Arrays.copyOfRange(parts, 1, parts.length); |
| } |
| JSONArray modules = getModules(); |
| JSONObject module = null; |
| if (modules != null) { |
| for (Object moduleObject : modules) { |
| module = (JSONObject) moduleObject; |
| String name = getJSONStringProperty(module, NAME); |
| if (name != null && name.equals(parts[0])) { |
| break; |
| } |
| module = null; |
| } |
| } |
| if (module != null) { |
| JSONObject property = module; |
| for (int i = 1; i < parts.length; i++) { |
| if (NodeJsUtils.PROTOTYPE.equals(parts[i]) |
| || NodeJsUtils.EXPORTS.equals(parts[i]) |
| || NodeJsUtils.MODULE.equals(parts[i])) { |
| continue; |
| } |
| property = findProperty(property, parts[i]); |
| if (property == null) { |
| break; |
| } |
| } |
| return property == null ? null : getJSONStringProperty(property, DESCRIPTION); |
| } |
| return null; |
| } |
| |
| private JSONObject findProperty(final JSONObject parent, final String name) { |
| JSONArray properties = getJSONArrayProperty(parent, PROPERTIES); |
| if (properties != null) { |
| for (Object propertyTmp : properties) { |
| JSONObject property = (JSONObject) propertyTmp; |
| String propertyName = getJSONStringProperty(property, NAME); |
| if (propertyName != null && propertyName.equals(name)) { |
| return property; |
| } |
| } |
| } |
| properties = getJSONArrayProperty(parent, METHODS); |
| if (properties != null) { |
| for (Object propertyTmp : properties) { |
| JSONObject property = (JSONObject) propertyTmp; |
| String propertyName = getJSONStringProperty(property, NAME); |
| if (propertyName != null && propertyName.equals(name)) { |
| return property; |
| } |
| } |
| } |
| properties = getJSONArrayProperty(parent, CLASSES); |
| if (properties != null) { |
| String className = getJSONStringProperty(parent, NAME) + '.' + name; |
| for (Object propertyTmp : properties) { |
| JSONObject property = (JSONObject) propertyTmp; |
| String propertyName = getJSONStringProperty(property, NAME); |
| if (propertyName != null && (propertyName.equals(className) || propertyName.equals(name))) { |
| return property; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private class ProjectSupportChangeListener implements ChangeListener { |
| private final Project project; |
| |
| public ProjectSupportChangeListener(Project project) { |
| this.project = project; |
| } |
| |
| @Override |
| public void stateChanged(ChangeEvent e) { |
| cache.remove(project); |
| } |
| |
| } |
| |
| } |