/* * Copyright (c) 2013, the Dart project authors. * * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html * * 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 com.google.dart.tools.core.pub; import com.google.common.reflect.TypeToken; import com.google.dart.tools.core.DartCore; import com.google.dart.tools.core.utilities.yaml.PubYamlUtils; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.lang.reflect.Type; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; /** * Query pub.dartlang.org and get the information for the packages */ public class PubPackageManager { /** * Save package information from pub.dartlang to disk */ private class PubPackageListWriter implements IPubPackageListener { @Override public void pubPackagesChanged(List<PubPackageObject> pubPackages) { Gson gson = new GsonBuilder().create(); Type collectionType = new TypeToken<Collection<PubPackageObject>>() { }.getType(); String json = gson.toJson(pubPackages, collectionType); File file = getPackagesFile(); PrintWriter printWriter = null; try { printWriter = new PrintWriter(new FileWriter(file)); printWriter.write(json); } catch (IOException e) { DartCore.logInformation("Exception while saving pub package info", e); } finally { if (printWriter != null) { printWriter.close(); } } } } private static final PubPackageManager INSTANCE = new PubPackageManager(); public static final PubPackageManager getInstance() { return INSTANCE; } private List<String> packagesList = new ArrayList<String>(); private List<PubPackageObject> pubPackages = new ArrayList<PubPackageObject>(); private PubPackageListWriter writer = new PubPackageListWriter(); private final ListenerList listeners = new ListenerList(); /** * Used to synchronize access to webPackages */ private Object lock = new Object(); private Job job; public PubPackageManager() { listeners.add(writer); } public void addListener(IPubPackageListener listener) { listeners.add(listener); } /** * Return a list containing the names of the packages on pub */ public Collection<String> getPackageList() { if (packagesList.isEmpty()) { initialize(); } synchronized (lock) { return new ArrayList<String>(packagesList); } } /** * Return an array containing the names of the packages on pub */ public String[] getPackageListArray() { Collection<String> copy = getPackageList(); return copy.toArray(new String[copy.size()]); } /** * Return an array of {@link PubPackageObject}, the packages on pub */ public List<PubPackageObject> getPubPackages() { if (pubPackages.isEmpty()) { initialize(); } synchronized (lock) { return new ArrayList<PubPackageObject>(pubPackages); } } public void initialize() { readPackagesFromFile(); startPackageListFromPubJob(); } public void notifyListeners(List<PubPackageObject> packages) { for (Object listener : listeners.getListeners()) { ((IPubPackageListener) listener).pubPackagesChanged(packages); } } public void removeListener(IPubPackageListener listener) { listeners.remove(listener); } public void startPackageListFromPubJob() { if (job == null || job.getState() == Job.NONE) { job = new Job("Get package list from pub") { @Override protected IStatus run(IProgressMonitor monitor) { try { return fillPackageList(monitor); } catch (Exception e) { DartCore.logError(e); } return Status.OK_STATUS; } }; job.setSystem(true); job.schedule(6000); } } public void stop() { if (job != null) { // Try to cancel the job, but if it can't be cancelled cleanly then it will be cancelled when // the editor exits. job.cancel(); } } /** * Get the data from pub.dartlang.org */ private IStatus fillPackageList(IProgressMonitor monitor) throws Exception { int pageCount = 1; String line = null; JSONArray jsonArray = new JSONArray(); for (int page = 1; page <= pageCount; page++) { URLConnection connection = getApiUrl2(page); InputStream is; try { is = connection.getInputStream(); } catch (UnknownHostException e) { // No internet connection... just exit break; } catch (IOException e) { // server error ... just exit break; } BufferedReader br = new BufferedReader(new InputStreamReader(is)); try { while ((line = br.readLine()) != null && !monitor.isCanceled()) { try { JSONObject object = new JSONObject(line); if (object != null) { pageCount = object.getInt("pages"); JSONArray packages = (JSONArray) object.get("packages"); jsonArray.put(packages); } } catch (JSONException e) { DartCore.logError("Failed to process '" + line + "' on page " + page + " from " + connection.getURL(), e); } } } finally { br.close(); } if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } } return processData(jsonArray, monitor); } /** * pub.dartlang apiv2 - returns more info for each package */ private URLConnection getApiUrl2(int page) throws IOException, MalformedURLException { return new URL("https://pub.dartlang.org/api/packages?page=" + page).openConnection(); } private File getPackagesFile() { File file = DartCore.getPlugin().getStateLocation().append("pub_packages.json").toFile(); return file; } // {"new_version_url":"http://pub.dartlang.org/api/packages/mongo_dart_query/versions/new", // "name":"mongo_dart_query","uploaders_url":"http://pub.dartlang.org/api/packages/mongo_dart_query/uploaders", // "latest":{"new_dartdoc_url":"http://pub.dartlang.org/api/packages/mongo_dart_query/versions/0.1.8/new_dartdoc", // "pubspec":{"author":"Vadim Tsushko <vadimtsushko@gmail.com>","dev_dependencies":{"unittest":"any","browser":"any"}, // "dependencies":{"bson":">=0.1.7 <2.0.0"},"description":"Query builder for mongo_dart and objectory", // "name":"mongo_dart_query","homepage":"https://github.com/vadimtsushko/mongo_dart_query","version":"0.1.8"}, // "archive_url":"http://pub.dartlang.org/packages/mongo_dart_query/versions/0.1.8.tar.gz", // "package_url":"http://pub.dartlang.org/api/packages/mongo_dart_query", // "url":"http://pub.dartlang.org/api/packages/mongo_dart_query/versions/0.1.8","version":"0.1.8"}, // "version_url":"http://pub.dartlang.org/api/packages/mongo_dart_query/versions/{version}", // "url":"http://pub.dartlang.org/api/packages/mongo_dart_query"} // private IStatus processData(JSONArray jsonArray, IProgressMonitor monitor) { List<PubPackageObject> packageObjectList = new ArrayList<PubPackageObject>(); List<String> packageNames = new ArrayList<String>(); for (int j = 0; j < jsonArray.length(); j++) { JSONArray packages; String name = null; try { packages = jsonArray.getJSONArray(j); for (int i = 0; i < packages.length(); i++) { JSONObject o = new JSONObject(packages.getString(i)); name = o.getString(PubspecConstants.NAME); Map<String, Object> pubspec = PubYamlUtils.parsePubspecYamlToMap(o.getJSONObject("latest").getString( "pubspec")); PubPackageObject obj = new PubPackageObject( name, (String) pubspec.get(PubspecConstants.DESCRIPTION), (String) pubspec.get(PubspecConstants.VERSION), o.getString("url")); packageObjectList.add(obj); packageNames.add(name); } } catch (JSONException e) { DartCore.logError( "Failed to process pub list response: name=" + name + "\n" + jsonArray, e); } if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } } synchronized (lock) { pubPackages = packageObjectList; packagesList = packageNames; } notifyListeners(getPubPackages()); return Status.OK_STATUS; } /** * Reads the packages information stored in metadata */ private void readPackagesFromFile() { File file = getPackagesFile(); if (file.exists()) { Gson gson = new GsonBuilder().create(); Type collectionType = new TypeToken<Collection<PubPackageObject>>() { }.getType(); Reader reader = null; try { reader = new InputStreamReader(new FileInputStream(file), "UTF-8"); List<PubPackageObject> packages = gson.fromJson(reader, collectionType); List<String> packageNames = new ArrayList<String>(); for (PubPackageObject p : packages) { packageNames.add(p.getName()); } if (!packages.isEmpty()) { synchronized (lock) { pubPackages = packages; packagesList = packageNames; } } } catch (UnsupportedEncodingException e) { } catch (FileNotFoundException e) { } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { } } } } } }