diff --git a/app/lib/org.eclipse.jgit-4.3.0.201604071810-r.jar b/app/lib/org.eclipse.jgit-4.3.0.201604071810-r.jar
new file mode 100644
index 00000000000..d0a4070b5fb
Binary files /dev/null and b/app/lib/org.eclipse.jgit-4.3.0.201604071810-r.jar differ
diff --git a/app/lib/slf4j-api-1.7.21.jar b/app/lib/slf4j-api-1.7.21.jar
new file mode 100644
index 00000000000..7811fa0a832
Binary files /dev/null and b/app/lib/slf4j-api-1.7.21.jar differ
diff --git a/app/lib/slf4j-simple-1.7.21.jar b/app/lib/slf4j-simple-1.7.21.jar
new file mode 100644
index 00000000000..da46fee9cbb
Binary files /dev/null and b/app/lib/slf4j-simple-1.7.21.jar differ
diff --git a/app/src/cc/arduino/view/git/GitDiffForm.form b/app/src/cc/arduino/view/git/GitDiffForm.form
new file mode 100644
index 00000000000..b7686ad6d87
--- /dev/null
+++ b/app/src/cc/arduino/view/git/GitDiffForm.form
@@ -0,0 +1,76 @@
+
+
+
diff --git a/app/src/cc/arduino/view/git/GitDiffForm.java b/app/src/cc/arduino/view/git/GitDiffForm.java
new file mode 100644
index 00000000000..7c9a100aac5
--- /dev/null
+++ b/app/src/cc/arduino/view/git/GitDiffForm.java
@@ -0,0 +1,145 @@
+/*
+ * This file is part of Arduino.
+ *
+ * Copyright 2015 Arduino LLC (http://www.arduino.cc/)
+ *
+ * Arduino is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * As a special exception, you may use this file as part of a free software
+ * library without restriction. Specifically, if other files instantiate
+ * templates or use macros or inline functions from this file, or you compile
+ * this file and link it with other files to produce an executable, this
+ * file does not by itself cause the resulting executable to be covered by
+ * the GNU General Public License. This exception does not however
+ * invalidate any other reasons why the executable file might be covered by
+ * the GNU General Public License.
+ */
+
+package cc.arduino.view.git;
+
+import processing.app.git.GitOutputParser;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultHighlighter;
+import javax.swing.text.Highlighter;
+
+import java.awt.*;
+
+import static processing.app.I18n.tr;
+
+public class GitDiffForm extends javax.swing.JFrame {
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ jScrollPane1 = new javax.swing.JScrollPane();
+ oldTextPane = new javax.swing.JTextPane();
+ jScrollPane2 = new javax.swing.JScrollPane();
+ newTextPane = new javax.swing.JTextPane();
+
+ setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+ setTitle(tr("Diff"));
+ setResizable(false);
+
+ jScrollPane1.setViewportView(oldTextPane);
+ oldTextPane.setEditable(false);
+ jScrollPane2.setViewportView(newTextPane);
+ newTextPane.setEditable(false);
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(25, 25, 25)
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 400, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(18, 18, 18)
+ .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 395, Short.MAX_VALUE)
+ .addContainerGap())
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 427, Short.MAX_VALUE)
+ .addComponent(jScrollPane2))
+ .addContainerGap())
+ );
+
+ pack();
+ }// //GEN-END:initComponents
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JScrollPane jScrollPane1;
+ private javax.swing.JScrollPane jScrollPane2;
+ private javax.swing.JTextPane newTextPane;
+ private javax.swing.JTextPane oldTextPane;
+ // End of variables declaration//GEN-END:variables
+
+ private Color addedColor = Color.GREEN;
+ private Color deletedColor = Color.PINK;
+
+ private Highlighter.HighlightPainter addedPainter = new DefaultHighlighter.DefaultHighlightPainter(addedColor);
+ private Highlighter.HighlightPainter deletedPainter = new DefaultHighlighter.DefaultHighlightPainter(deletedColor);
+
+ public GitDiffForm(GitOutputParser.ParserResult parserResult) {
+ initComponents();
+
+ GitOutputParser.DiffText newText = parserResult.getNewText();
+ GitOutputParser.DiffText oldText = parserResult.getOldText();
+
+ newTextPane.setText(newText.getText());
+ oldTextPane.setText(oldText.getText());
+
+ for (int i = 0; i < newText.getBeginIndexes().size(); i++) {
+ highlightNew(
+ newText.getBeginIndexes().get(i),
+ newText.getEndIndexes().get(i)
+ );
+ }
+
+ for (int i = 0; i < oldText.getBeginIndexes().size(); i++) {
+ highlightOld(
+ oldText.getBeginIndexes().get(i),
+ oldText.getEndIndexes().get(i)
+ );
+ }
+
+ }
+
+ public void highlightNew(int begin, int end) {
+ try {
+ newTextPane.getHighlighter().addHighlight(begin, end, addedPainter);
+ } catch (BadLocationException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void highlightOld(int begin, int end) {
+ try {
+ oldTextPane.getHighlighter().addHighlight(begin, end, deletedPainter);
+ } catch (BadLocationException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java
index cc7356b4e81..a96f03f3c26 100644
--- a/app/src/processing/app/Editor.java
+++ b/app/src/processing/app/Editor.java
@@ -39,6 +39,7 @@
import org.fife.ui.rtextarea.RTextScrollPane;
import processing.app.debug.RunnerException;
import processing.app.forms.PasswordAuthorizationDialog;
+import processing.app.git.GitManager;
import processing.app.helpers.Keys;
import processing.app.helpers.OSUtils;
import processing.app.helpers.PreferencesMapException;
@@ -160,7 +161,7 @@ public boolean test(Sketch sketch) {
static volatile AbstractMonitor serialMonitor;
static AbstractMonitor serialPlotter;
-
+
final EditorHeader header;
EditorStatus status;
EditorConsole console;
@@ -202,6 +203,10 @@ public boolean test(Sketch sketch) {
private Runnable exportAppHandler;
private Runnable timeoutUploadHandler;
+ private GitManager gitManager = new GitManager();
+
+
+
public Editor(Base ibase, File file, int[] storedLocation, int[] defaultLocation, Platform platform) throws Exception {
super("Arduino");
this.base = ibase;
@@ -252,7 +257,7 @@ public void windowDeactivated(WindowEvent e) {
//PdeKeywords keywords = new PdeKeywords();
//sketchbook = new Sketchbook(this);
-
+
buildMenuBar();
// For rev 0120, placing things inside a JPanel
@@ -300,7 +305,7 @@ public void windowDeactivated(WindowEvent e) {
scrollPane.setViewportBorder(BorderFactory.createEmptyBorder());
scrollPane.setLineNumbersEnabled(PreferencesData.getBoolean("editor.linenumbers"));
scrollPane.setIconRowHeaderEnabled(false);
-
+
Gutter gutter = scrollPane.getGutter();
gutter.setBookmarkingEnabled(false);
//gutter.setBookmarkIcon(CompletionsRenderer.getIcon(CompletionType.TEMPLATE));
@@ -572,6 +577,7 @@ public void menuSelected(MenuEvent e) {
menubar.add(toolsMenu);
menubar.add(buildHelpMenu());
+ menubar.add(buildGitMenu());
setJMenuBar(menubar);
}
@@ -696,6 +702,59 @@ public void actionPerformed(ActionEvent e) {
return fileMenu;
}
+ private JMenu buildGitMenu() {
+ JMenu gitMenu = new JMenu(tr("Git"));
+ gitMenu.setMnemonic(KeyEvent.VK_G);
+
+ JMenuItem item = newJMenuItem(tr("Init"), 'I');
+ item.addActionListener(e -> gitManager
+ .init(sketch.getFolder())
+ );
+ gitMenu.add(item);
+
+ item = newJMenuItem(tr("Commit"), 'C');
+ item.addActionListener(e -> {
+ String commitMessage = JOptionPane.showInputDialog("Commit message:");
+ gitManager.commit(sketch.getFolder(), commitMessage);
+ });
+ gitMenu.add(item);
+
+ item = newJMenuItem(tr("Log"), 'L');
+ item.addActionListener(e ->
+ gitManager.log(sketch.getFolder())
+ );
+ gitMenu.add(item);
+
+ item = newJMenuItem(tr("Diff"), 'D');
+ item.addActionListener(e -> {
+ gitManager.diff(sketch.getFolder(), sketch.getCurrentCode().getFile());
+ });
+ gitMenu.add(item);
+
+ item = newJMenuItem(tr("Reset"), 'R');
+ item.addActionListener(e -> {
+ int i = JOptionPane.showConfirmDialog(
+ null,
+ tr("This is revert all changes.\n Are you sure?"),
+ tr("alert"),
+ JOptionPane.OK_CANCEL_OPTION
+ );
+ if (i == JOptionPane.OK_OPTION) {
+ gitManager.reset(sketch.getFolder(), sketch.getCurrentCode().getFile());
+ }
+ // Update the currently visible program with its code
+ try {
+ sketch.load(true);
+ textarea.setText(sketch.getCurrentCode().getProgram());
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ });
+ gitMenu.add(item);
+
+ return gitMenu;
+ }
+
public void rebuildRecentSketchesMenu() {
recentSketchesMenu.removeAll();
for (JMenuItem recentSketchMenuItem : base.getRecentSketchesMenuItems()) {
@@ -1598,7 +1657,7 @@ public void actionPerformed(ActionEvent e) {
}
protected void updateUndoState() {
-
+
UndoManager undo = textarea.getUndoManager();
if (undo.canUndo()) {
@@ -1634,7 +1693,7 @@ public void actionPerformed(ActionEvent e) {
protected void updateRedoState() {
UndoManager undo = textarea.getUndoManager();
-
+
if (undo.canRedo()) {
redoItem.setEnabled(true);
redoItem.setText(undo.getRedoPresentationName());
@@ -1797,7 +1856,7 @@ protected void setCode(final SketchCodeDocument codeDoc) {
}
// set up this guy's own undo manager
// code.undo = new UndoManager();
-
+
codeDoc.setDocument(document);
}
@@ -1805,17 +1864,17 @@ protected void setCode(final SketchCodeDocument codeDoc) {
codeDoc.setUndo(new LastUndoableEditAwareUndoManager(textarea, this));
document.addUndoableEditListener(codeDoc.getUndo());
}
-
+
// Update the document object that's in use
textarea.switchDocument(document, codeDoc.getUndo());
-
+
// HACK multiple tabs: for update Listeners of Gutter, forcin call: Gutter.setTextArea(RTextArea)
// BUG: https://github.com/bobbylight/RSyntaxTextArea/issues/84
scrollPane.setViewportView(textarea);
-
+
textarea.select(codeDoc.getSelectionStart(), codeDoc.getSelectionStop());
textarea.requestFocus(); // get the caret blinking
-
+
final int position = codeDoc.getScrollPosition();
// invokeLater: Expect the document to be rendered correctly to set the new position
@@ -2562,7 +2621,7 @@ public void handleSerial() {
return;
}
}
-
+
if (serialMonitor != null) {
// The serial monitor already exists
@@ -2651,7 +2710,7 @@ public void handleSerial() {
} while (serialMonitor.requiresAuthorization() && !success);
}
-
+
public void handlePlotter() {
if(serialMonitor != null) {
if(serialMonitor.isClosed()) {
@@ -2661,7 +2720,7 @@ public void handlePlotter() {
return;
}
}
-
+
if (serialPlotter != null) {
// The serial plotter already exists
@@ -2982,7 +3041,7 @@ private void configurePopupMenu(final SketchTextArea textarea){
item.setName("menuToolsAutoFormat");
menu.add(item);
-
+
item = newJMenuItem(tr("Comment/Uncomment"), '/');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
@@ -3026,7 +3085,7 @@ public void actionPerformed(ActionEvent e) {
final JMenuItem referenceItem = new JMenuItem(tr("Find in Reference"));
referenceItem.addActionListener(this::handleFindReference);
- menu.add(referenceItem);
+ menu.add(referenceItem);
final JMenuItem openURLItem = new JMenuItem(tr("Open URL"));
openURLItem.addActionListener(new ActionListener() {
@@ -3034,15 +3093,15 @@ public void actionPerformed(ActionEvent e) {
Base.openURL(e.getActionCommand());
}
});
- menu.add(openURLItem);
-
+ menu.add(openURLItem);
+
menu.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
String referenceFile = base.getPdeKeywords().getReference(getCurrentKeyword());
referenceItem.setEnabled(referenceFile != null);
-
+
int offset = textarea.getCaretPosition();
org.fife.ui.rsyntaxtextarea.Token token = RSyntaxUtilities.getTokenAtOffset(textarea, offset);
if (token != null && token.isHyperlink()) {
@@ -3075,4 +3134,6 @@ public void goToLine(int line) {
}
}
+
+
}
diff --git a/app/src/processing/app/git/GitManager.java b/app/src/processing/app/git/GitManager.java
new file mode 100644
index 00000000000..0579593aee0
--- /dev/null
+++ b/app/src/processing/app/git/GitManager.java
@@ -0,0 +1,198 @@
+package processing.app.git;
+
+import cc.arduino.view.git.GitDiffForm;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.NoHeadException;
+import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.diff.DiffFormatter;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+
+import java.io.*;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.function.Consumer;
+
+
+public class GitManager {
+
+ public void init(File repoDir) {
+ try (Git git = Git.init().setDirectory(repoDir).call()) {
+ System.out.println("Having repository: " + git.getRepository().getDirectory());
+ } catch (GitAPIException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void reset(File repoDir, File file) {
+ Consumer resetCommand = git -> {
+ try {
+ // We need get relative repository path
+ git.checkout().addPath(
+ getRelativePath(repoDir, file)
+ ).call();
+
+ System.out.println("Reset file " + file.getName());
+ } catch (GitAPIException e) {
+ e.printStackTrace();
+ }
+ };
+ runGitCommand(repoDir, resetCommand);
+ }
+
+ private String getRelativePath(File repoDir, File file) {
+ Path pathAbsolute = Paths.get(file.getPath());
+ Path pathBase = Paths.get(repoDir.getPath());
+ Path pathRelative = pathBase.relativize(pathAbsolute);
+ return pathRelative.toString();
+ }
+
+ public void diff(File repoDir, File file) {
+ Consumer commitCommand = git -> {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ try (DiffFormatter diffFormatter = new DiffFormatter(outputStream)) {
+ diffFormatter.setContext(20);
+ diffFormatter.setRepository(git.getRepository());
+ diffFormatter.setPathFilter(PathFilter.create(
+ getRelativePath(repoDir, file)
+ ));
+
+ AbstractTreeIterator commitTreeIterator = prepareTreeParser(git.getRepository(), Constants.HEAD);
+ FileTreeIterator workTreeIterator = new FileTreeIterator(git.getRepository());
+
+ List diffEntries = diffFormatter.scan(commitTreeIterator, workTreeIterator);
+ diffFormatter.format(diffEntries);
+
+ GitOutputParser parser = new GitOutputParser();
+ GitOutputParser.ParserResult result = parser.diffParser(new ByteArrayInputStream(outputStream.toByteArray()));
+
+ GitDiffForm gitDiffForm = new GitDiffForm(result);
+ gitDiffForm.setVisible(true);
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ };
+ runGitCommand(repoDir, commitCommand);
+ }
+
+ private AbstractTreeIterator prepareTreeParser(Repository repository, String objectId) throws IOException {
+ // from the commit we can build the tree which allows us to construct the TreeParser
+ try (RevWalk walk = new RevWalk(repository)) {
+ RevCommit commit = walk.parseCommit(repository.resolve(objectId));
+ RevTree tree = walk.parseTree(commit.getTree().getId());
+
+ CanonicalTreeParser oldTreeParser = new CanonicalTreeParser();
+ try (ObjectReader oldReader = repository.newObjectReader()) {
+ oldTreeParser.reset(oldReader, tree.getId());
+ }
+
+ walk.dispose();
+
+ return oldTreeParser;
+ }
+ }
+
+ public void commit(File repoDir, String message) {
+ if (!commitMessageIsValid(message)) {
+ System.err.println("Commit message isn't valid.");
+ return;
+ }
+
+ File[] files = repoDir.listFiles();
+
+ Consumer commitCommand = git -> {
+ try {
+ addFiles(files, git);
+ commit(message, git);
+ System.out.printf("Committed file to repository at: %s%n", git.getRepository().getDirectory());
+ } catch (GitAPIException e) {
+ e.printStackTrace();
+ }
+ };
+
+ runGitCommand(repoDir, commitCommand);
+ }
+
+ public void log(File repoDir) {
+
+ Consumer logCommand = git -> {
+ try {
+ Iterable logs = git.log()
+ .call();
+
+ int count = 0;
+ for (RevCommit rev : logs) {
+ System.out.printf("Commit: %s, name: %s%n", rev, rev.getName());
+ count++;
+ }
+
+ System.out.printf("Had %d commits overall on current branch%n", count);
+
+ } catch (NoHeadException e) {
+ System.err.printf("Repository %s is empty. Try to commit something.%n", git.getRepository().getDirectory());
+ } catch (GitAPIException e) {
+ e.printStackTrace();
+ }
+ };
+
+ runGitCommand(repoDir, logCommand);
+ }
+
+ private boolean commitMessageIsValid(String message) {
+ return !message.trim().isEmpty();
+ }
+
+ private void addFiles(File[] files, Git git) throws GitAPIException {
+ for (File file : files) {
+ git.add()
+ .addFilepattern(file.getName())
+ .call();
+ }
+ }
+
+ private void commit(String message, Git git) throws GitAPIException {
+ git.commit()
+ .setMessage(message)
+ .call();
+ }
+
+ private void runGitCommand(File repoDir, Consumer command) {
+ File gitDir = new File(repoDir, ".git");
+ if (gitDir.exists()) {
+ repoDir = gitDir;
+ }
+
+ FileRepositoryBuilder builder = new FileRepositoryBuilder();
+ try (Repository repository = builder.setGitDir(repoDir)
+ .readEnvironment() // scan environment GIT_* variables
+ .findGitDir(repoDir) // scan up the file system tree
+ .setMustExist(true)
+ .build()) {
+
+ try (Git git = new Git(repository)) {
+ command.accept(git);
+ }
+
+ } catch (RepositoryNotFoundException e) {
+ System.err.printf("Repository not found: %s. Try create git repository.%n", repoDir);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+}
diff --git a/app/src/processing/app/git/GitOutputParser.java b/app/src/processing/app/git/GitOutputParser.java
new file mode 100644
index 00000000000..afd999816e6
--- /dev/null
+++ b/app/src/processing/app/git/GitOutputParser.java
@@ -0,0 +1,93 @@
+package processing.app.git;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+public class GitOutputParser {
+
+ /**
+ * It divides diff output in two parts.
+ * @param in stream with diff text
+ * @return result of parsing
+ */
+ public ParserResult diffParser(InputStream in) {
+ DiffText newText = new DiffText();
+ DiffText oldText = new DiffText();
+
+ StringBuilder newBuilder = new StringBuilder();
+ StringBuilder oldBuilder = new StringBuilder();
+
+ Scanner scanner = new Scanner(in);
+ while (scanner.hasNext()) {
+ String string = scanner.nextLine();
+ if (string.startsWith("@@")) {
+ break;
+ }
+ }
+
+ while (scanner.hasNext()) {
+ String str = scanner.nextLine();
+
+ if (str.startsWith("+")) {
+ newText.getBeginIndexes().add(newBuilder.length());
+ newBuilder.append(str.substring(1)).append("\n");
+ newText.getEndIndexes().add(newBuilder.length());
+ continue;
+ }
+
+ if (str.startsWith("-")) {
+ oldText.getBeginIndexes().add(oldBuilder.length());
+ oldBuilder.append(str.substring(1)).append("\n");
+ oldText.getEndIndexes().add(oldBuilder.length());
+ continue;
+ }
+
+ newBuilder.append(str).append("\n");
+ oldBuilder.append(str).append("\n");
+ }
+
+ newText.text = newBuilder.toString();
+ oldText.text = oldBuilder.toString();
+
+ return new ParserResult(newText, oldText);
+ }
+
+ public static class ParserResult {
+ private DiffText newText;
+ private DiffText oldText;
+
+ public ParserResult(DiffText newText, DiffText oldText) {
+ this.newText = newText;
+ this.oldText = oldText;
+ }
+
+ public DiffText getNewText() {
+ return newText;
+ }
+
+ public DiffText getOldText() {
+ return oldText;
+ }
+ }
+
+ public static class DiffText {
+ private String text;
+ private List beginIndexes = new ArrayList<>();
+ private List endIndexes = new ArrayList<>();
+
+ public String getText() {
+ return text;
+ }
+
+ public List getBeginIndexes() {
+ return beginIndexes;
+ }
+
+ public List getEndIndexes() {
+ return endIndexes;
+ }
+ }
+
+}