| /* |
| * 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.requirejs; |
| |
| 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.URI; |
| 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.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import org.netbeans.api.progress.ProgressHandle; |
| import org.openide.modules.Places; |
| import org.openide.util.NbBundle; |
| import org.openide.util.RequestProcessor; |
| |
| /** |
| * |
| * @author Petr Pisl |
| */ |
| @NbBundle.Messages({ |
| "doc.building=Loading RequireJS Documentation", |
| "# {0} - the documentation URL", |
| "doc.cannotGet=Cannot load RequireJS documentation from \"{0}\".", |
| "doc.notFound=Documentation not found." |
| }) |
| public class RequireJsDataProvider { |
| |
| private static final Logger LOG = Logger.getLogger(RequireJsDataProvider.class.getSimpleName()); //NOI18N |
| private static RequestProcessor RP = new RequestProcessor(RequireJsDataProvider.class); |
| private static RequireJsDataProvider INSTANCE; |
| private boolean loadingStarted; |
| private ProgressHandle progress; |
| |
| private static final String CACHE_FOLDER_NAME = "requirejs-doc"; //NOI18N |
| private static final String API_FILE = "api.html"; //NOI18N |
| public static final String API_URL = "http://requirejs.org/docs/api.html"; |
| |
| 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 SEARCH_TEXT = "<p id=\"config-"; //NOI18N |
| |
| |
| /** |
| * Translating names from documentation to the real option names |
| */ |
| private static final HashMap<String, String> TRANSLATE_NAME = new HashMap<>(); |
| |
| static { |
| TRANSLATE_NAME.put("moduleconfig", "config");//NOI18N |
| // remove this option |
| TRANSLATE_NAME.put("map-notes", ""); //NOI18N |
| } |
| |
| private RequireJsDataProvider() { |
| loadingStarted = false; |
| } |
| |
| public static synchronized RequireJsDataProvider getDefault() { |
| if (INSTANCE == null) { |
| INSTANCE = new RequireJsDataProvider(); |
| } |
| return INSTANCE; |
| } |
| |
| public Collection<String> getConfigurationOptions() { |
| Collection<String> options = getConfigurationOptionsFromDoc(); |
| if (options.isEmpty()) { |
| options = getConfigurationOptionsStatic(); |
| } |
| return options; |
| } |
| |
| public String getDocForHtmlTagAttribute(final String attributeName) { |
| String api = getContentApiFile(); |
| if (api != null) { |
| String searchText = "href=\"#" + attributeName + "\" name=\"" + attributeName + "\""; //NOI18N |
| int start = api.indexOf(searchText); |
| if (start > 0) { |
| String tmpText = api.substring(0, start); |
| start = tmpText.lastIndexOf("<h3>"); // NOI18N |
| int end = api.indexOf("<h3>", start + 10); //NOI18N |
| if (start < end) { |
| return api.substring(start, end); |
| } |
| } |
| } |
| return ""; |
| } |
| |
| public String getDocFocOption(String name) { |
| String api = getContentApiFile(); |
| if (api != null) { |
| int index = api.indexOf(SEARCH_TEXT + name); |
| if (index == -1 && TRANSLATE_NAME.containsValue(name)) { |
| for (Map.Entry<String, String> entry : TRANSLATE_NAME.entrySet()) { |
| if (entry.getValue().equals(name)) { |
| index = api.indexOf(SEARCH_TEXT + entry.getKey()); |
| break; |
| } |
| |
| } |
| } |
| if (index > 0) { |
| int start = index; |
| index = api.indexOf(SEARCH_TEXT, start + SEARCH_TEXT.length()); |
| if (index == -1) { |
| index = api.indexOf("</div>", start + SEARCH_TEXT.length()); |
| } |
| if (index > 0) { |
| return api.substring(start, index); |
| } |
| } |
| } |
| return Bundle.doc_notFound(); |
| } |
| |
| private void startLoading() { |
| LOG.fine("start loading doc"); //NOI18N |
| |
| progress = ProgressHandle.createHandle(Bundle.doc_building()); |
| progress.start(1); |
| |
| } |
| |
| private Collection<String> getConfigurationOptionsFromDoc() { |
| List<String> result = new ArrayList<>(); |
| |
| String api = getContentApiFile(); |
| if (api != null) { |
| int index = api.indexOf(SEARCH_TEXT); |
| while (index > 0) { |
| int start = index + SEARCH_TEXT.length(); |
| index = api.indexOf('"', start); |
| String option = api.substring(start, index); |
| if (TRANSLATE_NAME.containsKey(option)) { |
| option = TRANSLATE_NAME.get(option); |
| } |
| if (!(option == null || option.isEmpty())) { |
| result.add(option); |
| } |
| index = api.indexOf(SEARCH_TEXT, index); |
| } |
| } |
| return result; |
| } |
| |
| public static File getCachedAPIFile() { |
| String pathFile = new StringBuilder().append(CACHE_FOLDER_NAME).append('/').append(API_FILE).toString(); |
| File cacheFile = Places.getCacheSubfile(pathFile); |
| return cacheFile; |
| } |
| |
| private String getContentApiFile() { |
| String result = null; |
| try { |
| File cacheFile = getCachedAPIFile(); |
| if (!cacheFile.exists()) { |
| |
| //if any of the files is not loaded yet, start the loading process |
| if (!loadingStarted) { |
| loadingStarted = true; |
| startLoading(); |
| } |
| //load from web and cache locally |
| loadDoc(cacheFile); |
| if (progress != null) { |
| progress.progress(1); |
| progress.finish(); |
| progress = null; |
| } |
| |
| LOG.log(Level.FINE, "Loading doc finished."); //NOI18N |
| } |
| result = getFileContent(cacheFile); |
| } catch (URISyntaxException | IOException ex) { |
| loadingStarted = false; |
| if (progress != null) { |
| progress.finish(); |
| progress = null; |
| } |
| LOG.log(Level.INFO, "Cannot load RequireJS documentation from \"{0}\".", new Object[]{API_URL}); //NOI18N |
| LOG.log(Level.INFO, "", ex); |
| } |
| return result; |
| } |
| |
| private Collection<String> getConfigurationOptionsStatic() { |
| List<String> known = new ArrayList<>(); |
| for (ConfigOption option : ConfigOption.values()) { |
| known.add(option.getName()); |
| } |
| return known; |
| } |
| |
| private void loadDoc(File cacheFile) throws URISyntaxException, MalformedURLException, IOException { |
| LOG.fine("start loading doc"); //NOI18N |
| URL url = new URI(API_URL).toURL(); |
| synchronized (cacheFile) { |
| String tmpFileName = cacheFile.getAbsolutePath() + ".tmp"; |
| File tmpFile = new File(tmpFileName); |
| try (Writer writer = new OutputStreamWriter(new FileOutputStream(tmpFile), StandardCharsets.UTF_8)) { |
| loadURL(url, writer, StandardCharsets.UTF_8); |
| writer.close(); |
| boolean success = tmpFile.renameTo(cacheFile); |
| if (!success) { |
| LOG.log(Level.WARNING, "Renaming {0} to {1] was not successful.", new Object[]{tmpFile.getAbsolutePath(), cacheFile.getAbsolutePath()}); |
| } |
| } finally { |
| if (tmpFile.exists()) { |
| boolean success = tmpFile.delete(); |
| if (!success) { |
| LOG.log(Level.WARNING, "Deleting {0} faild.", new Object[]{tmpFile.getAbsolutePath()}); |
| } |
| } |
| } |
| |
| } |
| } |
| |
| 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(); |
| 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); |
| } |
| r.close(); |
| } |
| |
| 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(); |
| } |
| } |