aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeil Guzman2013-07-25 16:05:40 (EDT)
committerAlexander Kurtakov2013-07-31 11:46:09 (EDT)
commitdc7c6fb9cdfd629467b1fe38e387668fff697cd7 (patch)
tree2c29f0aee55f946e89c51c54203e16e6ba5b2b1e
parent73e5e28fc1f81ee33925f096d512a9da6058c7ab (diff)
downloadorg.eclipse.linuxtools-dc7c6fb9cdfd629467b1fe38e387668fff697cd7.zip
org.eclipse.linuxtools-dc7c6fb9cdfd629467b1fe38e387668fff697cd7.tar.gz
org.eclipse.linuxtools-dc7c6fb9cdfd629467b1fe38e387668fff697cd7.tar.bz2
RPMStubby: Ruby Gem template B#350066refs/changes/74/14874/5
Grab the gemspec attributes to help create a specfile template. Need to be tested to see if template has basic stuff needed in creating an rpm from a gem. Amend: o formatting o changed general Exception to more specific ones o add if braces to one liners o changed Objects to Strings in parser o change exceptions and do not return nulls Change-Id: I5c4c4722a2afcf03156410a78c8424d2e67ee2c3 Signed-off-by: Neil Guzman <nguzman@redhat.com> Reviewed-on: https://git.eclipse.org/r/14874 Tested-by: Hudson CI Reviewed-by: Alexander Kurtakov <akurtako@redhat.com> IP-Clean: Alexander Kurtakov <akurtako@redhat.com> Tested-by: Alexander Kurtakov <akurtako@redhat.com>
-rw-r--r--rpmstubby/org.eclipse.linuxtools.rpmstubby/plugin.xml28
-rw-r--r--rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/StubbyGemGenerator.java186
-rw-r--r--rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/model/GemModel.java278
-rw-r--r--rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/parser/CommonMetaData.java2
-rw-r--r--rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/parser/RubyGemParser.java453
-rw-r--r--rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/popup/actions/StubifyGemHandler.java26
-rw-r--r--rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/rpmstubby/Generator.java6
-rw-r--r--rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/rpmstubby/InputType.java6
8 files changed, 981 insertions, 4 deletions
diff --git a/rpmstubby/org.eclipse.linuxtools.rpmstubby/plugin.xml b/rpmstubby/org.eclipse.linuxtools.rpmstubby/plugin.xml
index d3ee769..0324ab6 100644
--- a/rpmstubby/org.eclipse.linuxtools.rpmstubby/plugin.xml
+++ b/rpmstubby/org.eclipse.linuxtools.rpmstubby/plugin.xml
@@ -36,6 +36,12 @@
id="rpmstubby.stubifyEgg"
name="%action.label">
</command>
+ <command
+ categoryId="rpmstubby.commands"
+ description="%action.label"
+ id="rpmstubby.stubifyGem"
+ name="%action.label">
+ </command>
</extension>
<extension
@@ -52,6 +58,10 @@
class="org.eclipse.linuxtools.internal.rpmstubby.popup.actions.StubifyEggHandler"
commandId="rpmstubby.stubifyEgg">
</handler>
+ <handler
+ class="org.eclipse.linuxtools.internal.rpmstubby.popup.actions.StubifyGemHandler"
+ commandId="rpmstubby.stubifyGem">
+ </handler>
</extension>
<extension
point="org.eclipse.ui.menus">
@@ -111,6 +121,24 @@
</with>
</visibleWhen>
</command>
+ <command
+ commandId="rpmstubby.stubifyGem">
+ <visibleWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="org.eclipse.core.resources.IResource">
+ <test
+ property="org.eclipse.core.resources.name"
+ value="*.gemspec">
+ </test>
+ </adapt>
+ </iterate>
+ </with>
+ </visibleWhen>
+ </command>
</menuContribution>
</extension>
</plugin>
diff --git a/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/StubbyGemGenerator.java b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/StubbyGemGenerator.java
new file mode 100644
index 0000000..25f66c2
--- /dev/null
+++ b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/StubbyGemGenerator.java
@@ -0,0 +1,186 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Red Hat, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Neil Guzman - python, ruby implementation
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.rpmstubby;
+
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.linuxtools.internal.rpmstubby.model.GemModel;
+
+/**
+ * Generator for RPM specfile from ruby gemspec.
+ *
+ */
+public class StubbyGemGenerator extends AbstractGenerator {
+
+ private GemModel model;
+
+ private static final String FIX_ME = "#FIXME";
+
+ /**
+ * Creates the generator by parsing the gemspec file.
+ *
+ * @param gemFile
+ * The gemspec file to generate specfile for.
+ */
+ public StubbyGemGenerator(IFile gemFile) {
+ parse(gemFile);
+ specfileName = model.getPackageName().toLowerCase() + ".spec";
+ projectName = gemFile.getProject().getName();
+ }
+
+ /**
+ * Creates the model which contains the information
+ *
+ * @param gemFile The gemspec file
+ */
+ private void parse(IFile gemFile) {
+ model = new GemModel(gemFile);
+ }
+
+ /**
+ * Generates a RPM specfile based on the parsed data from the gemspec file.
+ *
+ * @return The generated specfile.
+ */
+ @Override
+ public String generateSpecfile() {
+ StringBuilder buffer = new StringBuilder();
+ generateDefines(buffer);
+ String packageName = model.getPackageName();
+ buffer.append("Name: " + packageName.toLowerCase() + "\n");
+ buffer.append("Version: " + model.getVersion() + "\n");
+ buffer.append("Release: 1%{?dist}" + "\n");
+ buffer.append("Summary: " + model.getSummary() + "\n\n");
+
+ buffer.append("Group: Development/Libraries\n");
+ buffer.append("License: " + model.getLicense() + "\n");
+ buffer.append("URL: " + model.getURL() + "\n");
+ buffer.append("Source0: #FIXME\n\n");
+
+ generateRequires(buffer);
+
+ buffer.append("BuildArch: noarch\n");
+ buffer.append("Provides: rubygem(%{gem_name}) = %{version}\n\n\n");
+
+ buffer.append("%description\n" + model.getDescription() + "\n\n\n");
+ generatePrepSection(buffer);
+ generateBuildSection(buffer);
+ generateInstallSection(buffer);
+ generateFilesSections(buffer);
+ generateChangelog(buffer);
+
+ return buffer.toString();
+ }
+
+ private void generateDefines(StringBuilder buffer) {
+ buffer.append("%global gem_name " + model.getSimplePackageName().toLowerCase() +"\n\n");
+ }
+
+ /**
+ * Generate requires
+ *
+ * @param buffer Buffer to write content to
+ */
+ private void generateRequires(StringBuilder buffer) {
+ buffer.append("BuildRequires: rubygems-devel\n");
+ List<String> requireList = model.getBuildRequiresList();
+ for (String require : requireList) {
+ buffer.append("BuildRequires: " + require + "\n");
+ }
+ buffer.append("Requires: ruby(release)\n");
+
+ String rubyGemsVersion = model.getGemVersion();
+ if (rubyGemsVersion.equals(FIX_ME)) {
+ buffer.append("Requires: rubygems\n");
+ } else {
+ buffer.append("Requires: rubygems == " + rubyGemsVersion +"\n");
+ }
+
+ requireList = model.getInstallRequiresList();
+ for (String require : requireList) {
+ buffer.append("Requires: " + require + "\n");
+ }
+ buffer.append("\n");
+ }
+
+ /**
+ * Generate prep
+ *
+ * @param buffer Buffer to write content to
+ */
+ private void generatePrepSection(StringBuilder buffer) {
+ buffer.append("%prep\n");
+ buffer.append("gem unpack %{SOURCE0}\n");
+ buffer.append("%setup -q -D -T -n %{gem_name}-%{version} #You may need to update this according to your Source0\n\n");
+
+ buffer.append("gem spec %{SOURCE0} -l --ruby > %{gem_name}.gemspec\n\n\n");
+ }
+
+ /**
+ * Generate build
+ *
+ * @param buffer Buffer to write content to
+ */
+ private void generateBuildSection(StringBuilder buffer) {
+ buffer.append("%build\n");
+ buffer.append("gem build %{gem_name}.gemspec\n\n");
+
+ buffer.append("%gem_install\n\n\n");
+ }
+
+ /**
+ * Generate install
+ *
+ * @param buffer Buffer to write content to
+ */
+ private void generateInstallSection(StringBuilder buffer) {
+ buffer.append("%install\n");
+ buffer.append("mkdir -p %{buildroot}%{gem_dir}\n");
+ buffer.append("cp -a ./%{gem_dir}/* %{buildroot}%{gem_dir}/\n\n");
+
+ buffer.append("mkdir -p %{buildroot}%{_bindir}\n");
+ buffer.append("cp -a ./%{_bindir}/* %{buildroot}%{_bindir}\n\n");
+
+ List<String> requirePaths = model.getRequirePaths();
+ if (requirePaths != null && requirePaths.size() > 0) {
+ buffer.append("mkdir -p %{buildroot}%{gem_extdir_mri}/"+ requirePaths.get(0) +"\n");
+ buffer.append("mv %{buildroot}%{gem_instdir}/"+requirePaths.get(0)+"/shared_object.so %{buildroot}%{gem_extdir_mri}/"+ requirePaths.get(0)+"/\n\n");
+ }
+
+ buffer.append("\n");
+ }
+
+ /**
+ * Generate files
+ *
+ * @param buffer Buffer to write content to
+ */
+ private void generateFilesSections(StringBuilder buffer) {
+ buffer.append("%files\n");
+ buffer.append("%dir %{gem_instdir}\n");
+ buffer.append("%{gem_libdir}\n");
+ buffer.append("%{gem_spec}\n\n");
+
+ buffer.append("%files doc\n");
+ buffer.append("%doc %{gem_docdir}\n\n\n");
+ }
+
+ /**
+ * Generate changelog
+ *
+ * @param buffer Buffer to write content to
+ */
+ private void generateChangelog(StringBuilder buffer) {
+ buffer.append("%changelog\n");
+ buffer.append("#FIXME\n");
+ }
+} \ No newline at end of file
diff --git a/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/model/GemModel.java b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/model/GemModel.java
new file mode 100644
index 0000000..bc4d4ce
--- /dev/null
+++ b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/model/GemModel.java
@@ -0,0 +1,278 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Red Hat, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Neil Guzman - python, ruby implementation
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.rpmstubby.model;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.linuxtools.internal.rpmstubby.StubbyLog;
+import org.eclipse.linuxtools.internal.rpmstubby.parser.CommonMetaData;
+import org.eclipse.linuxtools.internal.rpmstubby.parser.RubyGemParser;
+
+/**
+ * Gives easy access to data from the ruby gemspec file.
+ *
+ */
+public class GemModel {
+
+ private static final String FIX_ME = "#FIXME";
+
+ private static final String SUMMARY = "summary";
+ private static final String RUBYGEMS_VERSION = "rubygems_version";
+ private static final String REQUIRE_PATHS = "require_paths";
+ private static final String DEPENDENCY = "add_dependency";
+ private static final String DEVELOPMENT_DEPENDENCY = "add_development_dependency";
+ private static final String RUNTIME_DEPENDENCY = "add_runtime_dependency";
+ private static final String HOMEPAGE = "homepage";
+
+ /**
+ * Options in the gemspec file that are supposed to only have 1 value
+ *
+ */
+ private static final String[] singleValues = { CommonMetaData.DESCRIPTION,
+ CommonMetaData.NAME, CommonMetaData.VERSION,
+ CommonMetaData.LICENSE, SUMMARY, RUBYGEMS_VERSION, HOMEPAGE };
+
+ private RubyGemParser rbyGemParser;
+
+ /**
+ * Ruby gemspec file
+ *
+ * @param file
+ * The Ruby gemspec file
+ */
+ public GemModel(IFile file) {
+ try {
+ rbyGemParser = new RubyGemParser(file);
+ } catch (IOException e) {
+ StubbyLog.logError(e);
+ } catch (CoreException e) {
+ StubbyLog.logError(e);
+ }
+ }
+
+ /**
+ * Get the value from one of the gemspec attributes. If the value is empty, it
+ * will return #FIXME
+ *
+ * @param attr
+ * The gemspec attribute to get value of
+ * @return The value of the option
+ */
+ private String getValue(String attr) {
+ List<String> list = getValueList(attr);
+ String rc = "";
+ boolean single = false;
+
+ for (String str : singleValues) {
+ if (str.equals(attr)) {
+ single = true;
+ break;
+ }
+ }
+
+ if (!list.isEmpty() && single) {
+ rc = list.get(0);
+ } else {
+ rc = FIX_ME;
+ }
+
+ return rc;
+ }
+
+ /**
+ * Get the list of values from one of the gemspec attributes that stores a list
+ * of strings
+ *
+ * @param attr
+ * The gemspec attribute to get value of
+ * @return The list of values of the option
+ */
+ private List<String> getValueList(String option) {
+ return rbyGemParser.getValueList(option);
+ }
+
+ /**
+ * Get the install requires in the gemspec file
+ *
+ * @param key
+ * Either the dependencies or the development dependencies option
+ * @return A list of the values taken from the gemspec
+ */
+ public List<String> getDependencies(String key) {
+ List<String> rc = new ArrayList<String>();
+ List<String> temp = getValueList(key);
+
+ if (!temp.isEmpty()) {
+ for (int i = 0; i < temp.size(); i++) {
+ rc.add(temp
+ .get(i)
+ .replaceFirst("(?:%q|%Q)(?:([\\W])([^\\W]+)[\\W])",
+ "$2").replaceAll("(\"|'|\\[|\\])", "")
+ .replaceAll(",", ""));
+ }
+ }
+ return rc;
+ }
+
+ /**
+ * Get the values from the add_dependency option
+ *
+ * @return The values within add_dependency
+ */
+ public List<String> getInstallRequiresList() {
+ List<String> rc = getDependencies(DEPENDENCY);
+ rc.addAll(getDependencies(RUNTIME_DEPENDENCY));
+ return getDependencies(DEPENDENCY);
+ }
+
+ /**
+ * Get the values from the add_development_dependency option
+ *
+ * @return The values within add_development_dependency
+ */
+ public List<String> getBuildRequiresList() {
+ return getDependencies(DEVELOPMENT_DEPENDENCY);
+ }
+
+ /**
+ * Returns the package name.
+ *
+ * @return The package name.
+ */
+ public String getSimplePackageName() {
+ return getValue(CommonMetaData.NAME);
+ }
+
+ /**
+ * The simple package name with "rubygem-" prepended to make better RPM
+ * package name.
+ *
+ * @return The package
+ */
+ public String getPackageName() {
+ return "rubygem-" + getSimplePackageName();
+ }
+
+ /**
+ * Returns the version
+ *
+ * @return The version
+ */
+ public String getVersion() {
+ String version = getValue(CommonMetaData.VERSION);
+
+ if (!hasDigits(version)) {
+ version = "1 " + FIX_ME;
+ }
+
+ return version;
+ }
+
+ /**
+ * Returns the RubyGems version
+ *
+ * @return The RubyGems version
+ */
+ public String getGemVersion() {
+ String version = getValue(RUBYGEMS_VERSION);
+
+ if (!hasDigits(version)) {
+ version = FIX_ME;
+ }
+
+ return version;
+ }
+
+ /**
+ * Utility method to try and see if a string contains digits within it
+ *
+ * @param str
+ * The string to check if it has digits
+ * @return True if string contains digits
+ */
+ public boolean hasDigits(String str) {
+ return str.matches(".*\\d.*");
+ }
+
+ /**
+ * Returns the summary
+ *
+ * @return The package summary
+ */
+ public String getSummary() {
+ return getValue(SUMMARY);
+ }
+
+ /**
+ * Returns the license
+ *
+ * @return The license
+ */
+ public String getLicense() {
+ String license = getValue("license");
+ List<String> list = getValueList("licenses");
+
+ if (license.equals(FIX_ME) && !list.isEmpty()) {
+ license = "";
+ for (String str : list) {
+ license = license.concat(str + ", ");
+ license = license.replaceAll("(\"|')", "");
+ }
+ license = license.substring(0, license.length() - 2);
+ }
+
+ return license;
+ }
+
+ /**
+ * Returns the url
+ *
+ * @return The url
+ */
+ public String getURL() {
+ return getValue(HOMEPAGE);
+ }
+
+ /**
+ * Returns the description
+ *
+ * @return The description
+ */
+ public String getDescription() {
+ return getValue(CommonMetaData.DESCRIPTION);
+ }
+
+ /**
+ * Return the require paths
+ *
+ * @return The require paths
+ */
+ public List<String> getRequirePaths() {
+ List<String> rc = new ArrayList<String>();
+ List<String> temp = getValueList(REQUIRE_PATHS);
+
+ if (!temp.isEmpty()) {
+ for (int i = 0; i < temp.size(); i++) {
+ rc.add(temp
+ .get(i)
+ .replaceFirst("(?:%q|%Q)(?:([\\W])([^\\W]+)[\\W])",
+ "$2").replaceAll("(\"|'|\\[|\\])", "")
+ .replaceAll(",", ""));
+ }
+ }
+
+ return rc;
+ }
+}
diff --git a/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/parser/CommonMetaData.java b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/parser/CommonMetaData.java
index 5ebc57e..670a56c 100644
--- a/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/parser/CommonMetaData.java
+++ b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/parser/CommonMetaData.java
@@ -6,7 +6,7 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
- * Neil Guzman - python implementation
+ * Neil Guzman - python, ruby implementation
*******************************************************************************/
package org.eclipse.linuxtools.internal.rpmstubby.parser;
diff --git a/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/parser/RubyGemParser.java b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/parser/RubyGemParser.java
new file mode 100644
index 0000000..a3fac36
--- /dev/null
+++ b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/parser/RubyGemParser.java
@@ -0,0 +1,453 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Red Hat, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Neil Guzman - python, ruby implementation
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.rpmstubby.parser;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.linuxtools.internal.rpmstubby.StubbyLog;
+
+/**
+ * Class to parse a Ruby gemspec to grab specfile properties
+ *
+ */
+public class RubyGemParser {
+
+ private IFile file;
+ private String gemVariable;
+ private Map<String, ArrayList<String>> mSetupDefinitions;
+ private Map<String, ArrayList<String>> mSetupDependencies;
+
+ private static final String SETUP_START = "^gem[:\\.]{1,2}specification[:\\.]{1,2}new(\\s+)?do(\\s+)?\\|(\\s+)?(\\w+)(\\s+)?\\|";
+ private static final String STRING = "(?:\\\"([^\\\"]+)\\\"|'([^']+)'|(?:%q|%Q)(?:([^\\w])([^/3].+)[^\\w]))"; // (%q|%Q) "value" | 'value' | {value}
+ private static final String LIST = "(?!%q|%Q)(?:%w|%W)?(?:([\\W])(.+)[\\W])"; // (%w|%W) [value, value2] | {"value", "value2"}
+ private static final String STRING_LIST = "(?:\\\"([^\\\"]+)\\\"|'([^']+)')(?:[, ])??"; // "test", "test2" | 'test' 'test2'
+ private static final String GENERIC_LIST = "(?:\\S+)(?:\\s+)??"; // test, test2 | test test2
+
+ private static final String REPLACE_ME = "(%REPLACE_ME)";
+ private String SIMPLE_DEFINE = "(\\s+)?(?!#)(?:\\b(%REPLACE_ME)\\b\\.(\\w+))(\\s+)?=(?!=)(\\s+)?(.*)"; // gem.variable = ...
+ private String SIMPLE_FUNCTION = "(\\s+)?(?!#)(?:\\b(%REPLACE_ME)\\b\\.(\\w+))(\\s+)?(?:\\((.*)\\))(.*)?"; // gem.variable(...)
+ private String GENERIC_DEFINE = "(\\s+)?(?!#)(?:\\b(%REPLACE_ME)\\b\\.(\\w+))(\\s+)?(.*)"; // gem.variable...
+
+ /**
+ * Initialize
+ *
+ * @param file
+ * The gemspec file
+ * @throws IOException Could not read from file
+ * @throws CoreException File is not valid
+ */
+ public RubyGemParser(IFile file) throws IOException, CoreException {
+ mSetupDefinitions = new HashMap<String, ArrayList<String>>();
+ mSetupDependencies = new HashMap<String, ArrayList<String>>();
+ if (file.getContents().available() <= 0) {
+ return;
+ }
+ this.file = file;
+ gemVariable = "";
+ parse();
+ }
+
+ /**
+ * Parse the Ruby gemspec file
+ *
+ */
+ public void parse() {
+ List<String> rawSetupDefinitions = new ArrayList<String>();
+ List<String> lSetupDefinitions = new ArrayList<String>();
+ String line = "";
+ long startPos;
+ long endPos;
+ try {
+ RandomAccessFile raf = new RandomAccessFile(file.getRawLocation()
+ .makeAbsolute().toFile(), "r");
+ startPos = findStart(raf);
+ endPos = findEnd(raf, startPos);
+
+ raf.seek(startPos);
+ while ((line = raf.readLine()) != null
+ && raf.getFilePointer() < endPos) {
+ rawSetupDefinitions.add(line);
+ }
+
+ lSetupDefinitions = prepareOptions(rawSetupDefinitions);
+
+ for (String str : lSetupDefinitions) {
+ parseLine(str);
+ }
+ raf.close();
+ } catch (FileNotFoundException e) {
+ StubbyLog.logError(e);
+ } catch (IOException e) {
+ StubbyLog.logError(e);
+ }
+ }
+
+ /**
+ * Get the values taken from parsing the file as a list object
+ *
+ * @param key
+ * The gemspec option to get the values for
+ * @return The values of the gemspec option
+ */
+ public List<String> getValueList(String key) {
+ List<String> rc = new ArrayList<String>();
+
+ if (mSetupDependencies.containsKey(key)) {
+ if (!mSetupDependencies.get(key).isEmpty()) {
+ for (String element : mSetupDependencies.get(key)) {
+ rc.add(element);
+ }
+ }
+ } else if (mSetupDefinitions.containsKey(key)) {
+ if (!mSetupDefinitions.get(key).isEmpty()) {
+ for (String element : mSetupDefinitions.get(key)) {
+ rc.add(element);
+ }
+ }
+ }
+
+ return rc;
+ }
+
+ /**
+ * Parses a line to figure out what type of line it is
+ *
+ * @param str
+ * The line to parse
+ */
+ private void parseLine(String str) {
+ if (str.matches(SIMPLE_DEFINE)) {
+ parseSimpleDefine(str);
+ } else if (str.matches(SIMPLE_FUNCTION)) {
+ parseSimpleFunction(str);
+ } else if (str.matches(GENERIC_DEFINE)) {
+ parseGenericOption(str);
+ }
+ }
+
+ /**
+ * Parses a string to figure its value
+ *
+ * @param str
+ * The string parse
+ * @return A list of objects that was found
+ */
+ private List<String> parseValue(String str) {
+ List<String> rc = new ArrayList<String>();
+ String temp = str.trim();
+ Pattern pattern = null;
+ Matcher variableMatcher = null;
+
+ if (temp.matches(STRING)) {
+ pattern = Pattern.compile(STRING,
+ Pattern.CASE_INSENSITIVE);
+ variableMatcher = pattern.matcher(temp);
+ // "" matches group 1
+ if (temp.startsWith("\"") && variableMatcher.matches()) {
+ rc.add(variableMatcher.group(1));
+ } // '' matches group 2
+ else if (temp.startsWith("'") && variableMatcher.matches()) {
+ rc.add(variableMatcher.group(2));
+ } // %q|%Q match
+ else if ((temp.startsWith("%q") || temp.startsWith("%Q"))
+ && variableMatcher.matches()) {
+ rc.add(variableMatcher.group(4));
+ }
+ } else if (temp.matches(LIST)) {
+ pattern = Pattern.compile(LIST,
+ Pattern.CASE_INSENSITIVE);
+ variableMatcher = pattern.matcher(temp);
+ if (variableMatcher.matches()) {
+ rc.addAll(parseList(variableMatcher.group(2)));
+ }
+ }
+
+ return rc;
+ }
+
+ /**
+ * Parse the long string into a list
+ *
+ * @param str
+ * The string to parse into a list
+ * @return A list containing the found values
+ */
+ private List<String> parseList(String str) {
+ List<String> rc = new ArrayList<String>();
+ String temp = str.trim();
+ Pattern pattern = isPatternFoundList(str);
+
+ if (pattern == null) {
+ return rc;
+ }
+
+ Matcher variableMatcher = pattern.matcher(temp);
+
+ if (variableMatcher != null) {
+ while (variableMatcher.find()) {
+ rc.add(variableMatcher.group());
+ }
+ }
+ return rc;
+ }
+
+ /**
+ * Check if the string is a list of string values e.g. ["test", "test2"] OR
+ * ['test' 'test2']
+ *
+ * @param str
+ * The string containing the list
+ * @return The pattern of the string
+ */
+ private Pattern isPatternFoundList(String str) {
+ Pattern rc = Pattern.compile("");;
+ Pattern pattern = Pattern.compile(STRING_LIST,
+ Pattern.CASE_INSENSITIVE);
+ Matcher variableMatcher = pattern.matcher(str);
+
+ if (variableMatcher.find()) {
+ rc = pattern;
+ } else {
+ rc = isGenericFoundList(str);
+ }
+
+ return rc;
+ }
+
+ /**
+ * Check if the string is a list of generic values (non-strings) e.g. [test,
+ * test2] OR [test test2]
+ *
+ * @param str
+ * The string containing the list
+ * @return The pattern of the string
+ */
+ private Pattern isGenericFoundList(String str) {
+ Pattern rc = Pattern.compile("");
+ Pattern pattern = Pattern.compile(GENERIC_LIST,
+ Pattern.CASE_INSENSITIVE);
+ Matcher variableMatcher = pattern.matcher(str);
+
+ if (variableMatcher.find()) {
+ rc = pattern;
+ }
+
+ return rc;
+ }
+
+ /**
+ * Parse and grab the value of a simple define. The value taken is anything
+ * after "gem.variable = ..."
+ *
+ * @param str
+ * The simple define to parse
+ */
+ private void parseSimpleDefine(String str) {
+ String temp = str.trim();
+ Pattern pattern = Pattern.compile(SIMPLE_DEFINE,
+ Pattern.CASE_INSENSITIVE);
+ Matcher variableMatcher = pattern.matcher(temp);
+
+ if (variableMatcher.find()) {
+ String optionName = variableMatcher.group(2);
+ ArrayList<String> value = (ArrayList<String>) parseValue(variableMatcher
+ .group(5));
+ if (!value.isEmpty()) {
+ mSetupDefinitions.put(optionName, value);
+ }
+ }
+ }
+
+ /**
+ * Parse and grab the value of a simple function. The value taken is
+ * anything in "gem.variable(...)"
+ *
+ * @param str
+ * The function to parse
+ */
+ private void parseSimpleFunction(String str) {
+ String temp = str.trim();
+ Pattern pattern = Pattern.compile(SIMPLE_FUNCTION,
+ Pattern.CASE_INSENSITIVE);
+ Matcher variableMatcher = pattern.matcher(temp);
+
+ if (variableMatcher.find()) {
+ String functionName = variableMatcher.group(2);
+ ArrayList<String> dependencies = new ArrayList<String>();
+ dependencies.add(variableMatcher.group(4));
+ if (!mSetupDependencies.containsKey(functionName)) {
+ mSetupDependencies.put(functionName, dependencies);
+ } else {
+ if (!mSetupDependencies.get(functionName).containsAll(
+ dependencies)) {
+ mSetupDependencies.get(functionName).addAll(dependencies);
+ }
+ }
+ }
+ }
+
+ /**
+ * Parse and grab the value of a generic option. The value taken is anything
+ * after "gem.variable..."
+ *
+ * @param str
+ * The option to parse
+ */
+ private void parseGenericOption(String str) {
+ String temp = str.trim();
+ Pattern pattern = Pattern.compile(GENERIC_DEFINE,
+ Pattern.CASE_INSENSITIVE);
+ Matcher variableMatcher = pattern.matcher(temp);
+
+ if (variableMatcher.find()) {
+ String functionName = variableMatcher.group(2);
+ ArrayList<String> dependencies = new ArrayList<String>();
+ dependencies.add(variableMatcher.group(4));
+ if (!mSetupDependencies.containsKey(functionName)) {
+ mSetupDependencies.put(functionName, dependencies);
+ } else {
+ if (!mSetupDependencies.get(functionName).containsAll(
+ dependencies)) {
+ mSetupDependencies.get(functionName).addAll(dependencies);
+ }
+ }
+ }
+ }
+
+ /**
+ * Prepare the options within the specification by taking multiple lines and
+ * concatenating them to the option it belongs to
+ *
+ * @param list
+ * The list of options in their raw form
+ * @return A refined list of options with a single line for each option
+ */
+ private List<String> prepareOptions(List<String> list) {
+ List<String> rc = new ArrayList<String>();
+ String temp = "";
+
+ for (String str : list) {
+ temp = str.trim();
+ if (isLineValidOption(temp)) {
+ rc.add(str);
+ } else if (!temp.startsWith("#") && !rc.isEmpty()) {
+ rc.set(rc.size() - 1, rc.get(rc.size() - 1).concat(str));
+ }
+ }
+
+ return rc;
+ }
+
+ /**
+ * Check to see if the line being read is a valid option within the
+ * specification
+ *
+ * @param line
+ * The line to check
+ * @return True if the option within the specification is valid
+ */
+ private boolean isLineValidOption(String line) {
+ boolean rc = false;
+ Pattern pattern = Pattern.compile(GENERIC_DEFINE,
+ Pattern.CASE_INSENSITIVE);
+ Matcher variableMatcher = pattern.matcher(line);
+
+ if (variableMatcher.matches()) {
+ rc = true;
+ }
+
+ return rc;
+ }
+
+ /**
+ * Find the offset of when the specification starts and also store the
+ * gemVariable used in the specification
+ *
+ * @param reader
+ * The file reader
+ * @return The position of the start of the specification
+ * @throws IOException
+ */
+ private long findStart(RandomAccessFile raf) throws IOException {
+ long rc = -1;
+ Pattern pattern = Pattern
+ .compile(
+ SETUP_START,
+ Pattern.CASE_INSENSITIVE);
+ Matcher variableMatcher = null;
+ String line = "";
+
+ raf.seek(0);
+ while ((line = raf.readLine()) != null && rc == -1) {
+ variableMatcher = pattern.matcher(line.trim());
+ if (variableMatcher.matches()) {
+ setGemVariable(variableMatcher.group(4));
+ rc = raf.getFilePointer();
+ }
+ }
+
+ return rc;
+ }
+
+ /**
+ * Find the offset of when specification ends based on the position before
+ * the end statement
+ *
+ * @param reader
+ * The file reader
+ * @param startPosition
+ * The position of the start of the specification
+ * @return The position of the end of the specification
+ * @throws IOException
+ */
+ private long findEnd(RandomAccessFile raf, long startPos)
+ throws IOException {
+ long rc = -1;
+ Pattern pattern = Pattern.compile("^end", Pattern.CASE_INSENSITIVE);
+ Matcher variableMatcher = null;
+ String line = "";
+
+ raf.seek(startPos);
+ while ((line = raf.readLine()) != null && rc == -1) {
+ variableMatcher = pattern.matcher(line.trim());
+ if (variableMatcher.matches()) {
+ rc = raf.getFilePointer();
+ }
+ }
+
+ return rc;
+ }
+
+ /**
+ * Sets the gemVariable taken from Gem::Specification.new do |gemVariable|
+ *
+ * @param str
+ * The variable to set the gemVariable as
+ */
+ private void setGemVariable(String str) {
+ if (gemVariable.isEmpty()) {
+ gemVariable = str.trim();
+ SIMPLE_DEFINE = SIMPLE_DEFINE.replace(REPLACE_ME, gemVariable);
+ SIMPLE_FUNCTION = SIMPLE_FUNCTION.replace(REPLACE_ME, gemVariable);
+ GENERIC_DEFINE = GENERIC_DEFINE.replace(REPLACE_ME, gemVariable);
+ }
+ }
+}
diff --git a/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/popup/actions/StubifyGemHandler.java b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/popup/actions/StubifyGemHandler.java
new file mode 100644
index 0000000..747398b
--- /dev/null
+++ b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/internal/rpmstubby/popup/actions/StubifyGemHandler.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Red Hat, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Neil Guzman - python, ruby implementation
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.rpmstubby.popup.actions;
+
+import org.eclipse.linuxtools.rpmstubby.InputType;
+
+/**
+ * Handler for the gemspec stubify command.
+ *
+ */
+public class StubifyGemHandler extends StubifyHandler {
+
+ @Override
+ protected InputType getInputType() {
+ return InputType.RUBY_GEM;
+ }
+
+}
diff --git a/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/rpmstubby/Generator.java b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/rpmstubby/Generator.java
index f5737b7..18e5229 100644
--- a/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/rpmstubby/Generator.java
+++ b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/rpmstubby/Generator.java
@@ -7,13 +7,14 @@
*
* Contributors:
* Alexander Kurtakov - initial API and implementation
- * Neil Guzman - python implementation
+ * Neil Guzman - python, ruby implementation
*******************************************************************************/
package org.eclipse.linuxtools.rpmstubby;
import org.eclipse.core.resources.IFile;
import org.eclipse.linuxtools.internal.rpmstubby.StubbyEggGenerator;
+import org.eclipse.linuxtools.internal.rpmstubby.StubbyGemGenerator;
import org.eclipse.linuxtools.internal.rpmstubby.StubbyGenerator;
import org.eclipse.linuxtools.internal.rpmstubby.StubbyPomGenerator;
@@ -52,6 +53,9 @@ public class Generator {
case PYTHON_EGG:
new StubbyEggGenerator(file).writeContent();
break;
+ case RUBY_GEM:
+ new StubbyGemGenerator(file).writeContent();
+ break;
default:
break;
}
diff --git a/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/rpmstubby/InputType.java b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/rpmstubby/InputType.java
index 6a174cf..bba1bfa 100644
--- a/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/rpmstubby/InputType.java
+++ b/rpmstubby/org.eclipse.linuxtools.rpmstubby/src/org/eclipse/linuxtools/rpmstubby/InputType.java
@@ -7,7 +7,7 @@
*
* Contributors:
* Alexander Kurtakov - initial API and implementation
- * Neil Guzman - python implementation
+ * Neil Guzman - python, ruby implementation
*******************************************************************************/
package org.eclipse.linuxtools.rpmstubby;
@@ -23,7 +23,9 @@ public enum InputType {
/** Maven pom.xml file. */
MAVEN_POM("pom.xml"),
/** Python Egg setup.py file */
- PYTHON_EGG("setup.py");
+ PYTHON_EGG("setup.py"),
+ /** Ruby *.gemspec file */
+ RUBY_GEM("*.gemspec");
private String fileNamePattern;