Support free tutorials



vogella training Training Books

Eclipse Editor Plugin - Tutorial

Lars Vogel

Simon Scholz

Version 1.3

18.03.2015

How to create an Eclipse Editor

This tutorial describes how to create an editor and how this editor can be filled from a view. The tutorial is based on Eclipse 3.7 (Eclipse Indigo).


Table of Contents

1. Eclipse Editors
1.1. Overview
1.2. Steps required to create an editor
1.3. IEditorInput
1.4. IEditorPart and EditorPart
1.5. Setting the Editor Title und Tooltip
1.6. Saving the Editor Content
1.7. Editor area
1.8. API for working with Editors
2. Prerequisites for this tutorial
3. Exercise: Creation of an editor
3.1. Create project and data model
3.2. Editor Input
3.3. Adding the editor
3.4. Command for opening the editor
3.5. Task overview part
3.6. Validate
4. Texteditors and SourceViewer
5. Exercise: Create a TextEditor with content assist
5.1. Add new dependencies
5.2. Add Task Text Editor
5.3. Add simple content assist
5.4. Validate
6. About this website
6.1. Donate to support free tutorials
6.2. Questions and discussion
6.3. License for this tutorial and its code
7. Links and Literature
7.1. Source Code
7.2. Eclipse Resources
7.3. vogella Resources

1. Eclipse Editors

1.1. Overview

Eclipse uses editors and views to maintain data. An editor typically requires that the user explicitly select "save" to apply the changes to the data while a view typically changes the data immediately.

All editors are opened in the same area. Via the perspective you can configure if the editor area is visible or not.

1.2. Steps required to create an editor

The following steps are required to create and use an editor within an RCP application.

  • Make the editor area visible in your perspective

  • Create an IEditorInput class

  • Define an extension for the "org.eclipse.ui.editors" extension point

  • Implement the class for the editor, this class must implement IEditorPart

1.3. IEditorInput

IEditorInput serves as the model for the editor and is supposed to be a light-weight representation of the model. Eclipse will buffer IEditorInput objects therefore this object should be relatively small.

For example the Eclipse IDE uses IEditorInput objects to identify files without handling with the complete file. small.

Based on the equals() method of the IEditorInput the system will determine if the corresponding editor is already open or if a new editor must be opened.

1.4. IEditorPart and EditorPart

The editor is defined via the extension point "org.eclipse.ui.editors". The class which implement the editor must implement the interface "IEditorPart". In most cases it extends the abstract class "EditorPart".

The editor receives the IEditorSite and the IEditorInput in the init() method. It must set the input via the setInput() method and the side via the setSite() method.

init() is called before createPartControl() therefore you can use the input during your UI creation (which happens in createPartControl()).

1.5. Setting the Editor Title und Tooltip

By default the Editor will use the tooltip and title from the EditorInput.

Typically the EditorInput is only a light-weight representation of the real object. Therefore you may want to change the title and tooltip in your Editor. Use setPartName() to set the title of the Editor. To set the tooltip you have to override the method getTitleToolTip() (despite the Javadoc description). See Bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=107772 for details.

1.6. Saving the Editor Content

In an editor the method isDirty() is used to inform the workbench if the content of the editor is changed. For inform the workbench that the dirty property of the editor has changed you fired an event.

firePropertyChange(IEditorPart.PROP.DIRTY); 

1.7. Editor area

In a standalone RCP application you can make the editor area visible by changing your Perspective.java.

import org.eclipse.ui.IPageLayout;

public class Perspective implements IPerspectiveFactory {

  public void createInitialLayout(IPageLayout layout) {
    //layout.setEditorAreaVisible(false);
    layout.setFixed(true);
  }

} 

1.8. API for working with Editors

You can open an Editor via the current active page. For this you need the EditorInput object and the ID for the editor which is defined in the "org.eclipse.ui.editors" extension point.

page.openEditor(new YourEditorInput(), ID_OF_THE_EDITOR); 

To get the page you can use:

// If you are in a view
getViewSite().getPage();
// If you are in an command
HandlerUtil.getActiveWorkbenchWindow(event).getActivePage();
// Somewhere else
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); 

2. Prerequisites for this tutorial

This tutorial assumes that you have basic understanding of development for the Eclipse platform. Please see Eclipse RCP Tutorial or Eclipse Plug-in Tutorial if you need any basic information.

3. Exercise: Creation of an editor

This tutorials describes the creation of an editor. It also shows how an editor can interact with a view. We will create a view which shows several tasks.

3.1. Create project and data model

Create a new empty default RCP project "com.vogella.rcp.editor.example". Create the package "com.vogella.rcp.editor.example.model" and create the following classes in this package.

package com.vogella.rcp.editor.example.model;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class Task {
  private PropertyChangeSupport changes = new PropertyChangeSupport(this);

  public static final String FIELD_ID = "id";
  public static final String FIELD_SUMMARY = "summary";
  public static final String FIELD_DESCRIPTION = "description";
  public static final String FIELD_DONE = "done";
  public static final String FIELD_DUEDATE = "dueDate";
  public static final String FIELD_DEPENDENTS = "dependents";

  private long id;
  private String summary;
  private String description;
  private boolean done;
  private Date dueDate;
  private List<Long> dependents = new ArrayList<>();

  public Task(long i) {
    id = i;
  }

  public Task(long i, String summary, String description, boolean b, Date date) {
    this.id = i;
    this.summary = summary;
    this.description = description;
    this.done = b;
    this.dueDate = date;
  }

  public long getId() {
    return id;
  }

  public String getSummary() {
    return summary;
  }

  public void setSummary(String summary) {
    changes.firePropertyChange(FIELD_SUMMARY, this.summary, this.summary = summary);
  }

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    changes.firePropertyChange(FIELD_DESCRIPTION, this.description, this.description = description);
  }

  public boolean isDone() {
    return done;
  }

  public void setDone(boolean isDone) {
    changes.firePropertyChange(FIELD_DONE, this.done, this.done = isDone);
  }

  public Date getDueDate() {
    return dueDate;
  }

  public void setDueDate(Date dueDate) {
    changes.firePropertyChange(FIELD_DUEDATE, this.dueDate, this.dueDate = dueDate);
  }
  
  public List<Long> getDependentTasks() {
    return dependents;
  }
  
  public void setDependentTasks(List<Long> dependents) {
    this.dependents = dependents;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + (int) (id ^ (id >>> 32));
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    Task other = (Task) obj;
    if (id != other.id)
      return false;
    return true;
  }

  @Override
  public String toString() {
    return "Todo [id=" + id + ", summary=" + summary + "]";
  }

  public Task copy() {
    return new Task(id, summary, description, done, dueDate);
  }

  public void addPropertyChangeListener(PropertyChangeListener l) {
    changes.addPropertyChangeListener(l);
  }

  public void removePropertyChangeListener(PropertyChangeListener l) {
    changes.removePropertyChangeListener(l);
  }
} 

The following class will serve as a data model content provider.

package com.vogella.rcp.editor.example.model;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class TaskService {

  private static TaskService taskService = new TaskService();
  private List<Task> tasks = new ArrayList<Task>();

  private TaskService() {
    Task task = new Task(0, "Fix Bugs", "Fix Eclipse Bug 123", false, new Date());
    tasks.add(task);
    task = new Task(1, "Write Tutorial", "Write a tutorial about editors", true, new Date());
    tasks.add(task);
  }

  public static TaskService getInstance() {
    return taskService;
  }

  public List<Task> getTasks() {
    return tasks;
  }

  public Task getTaskById(long id) {
    for (Task todo : tasks) {
      if (todo.getId() == id) {
        return todo;
      }
    }
    return null;
  }
} 

3.2. Editor Input

Create the following new class TaskEditorInput which implements IEditorInput.

package com.vogella.rcp.editor.example.editor;

import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPersistableElement;

public class TaskEditorInput implements IEditorInput {

  private final long id;

  public TaskEditorInput(long id) {
    this.id = id;
  }

  public long getId() {
    return id;
  }

  @Override
  public boolean exists() {
    return true;
  }

  @Override
  public ImageDescriptor getImageDescriptor() {
    return null;
  }

  @Override
  public String getName() {
    return String.valueOf(id);
  }

  @Override
  public IPersistableElement getPersistable() {
    return null;
  }

  @Override
  public String getToolTipText() {
    return "Displays a task";
  }

  @Override
  public Object getAdapter(Class adapter) {
    return null;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + (int) (id ^ (id >>> 32));
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    TaskEditorInput other = (TaskEditorInput) obj;
    if (id != other.id)
      return false;
    return true;
  }
} 

3.3. Adding the editor

Go to plugin.xml and add the extension "org.eclipse.ui.editors".

Do not use a template. Use the ID "com.vogella.rcp.editor.example.editor.taskeditor", any name you want and the class "com.vogella.rcp.editor.example.editor.TaskEditor".

Click on the class hyperlink to create the class. Make sure the variable "ID" matches the ID of the editor extension.

package com.vogella.rcp.editor.example.editor;

import java.util.Date;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DateTime;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;

import com.vogella.rcp.editor.example.model.Task;
import com.vogella.rcp.editor.example.model.TaskService;

public class TaskEditor extends EditorPart {

  public static final String ID = "com.vogella.rcp.editor.example.editor.taskeditor";
  private Task todo;
  private TaskEditorInput input;

  // Will be called before createPartControl
  @Override
  public void init(IEditorSite site, IEditorInput input) throws PartInitException {
    if (!(input instanceof TaskEditorInput)) {
      throw new RuntimeException("Wrong input");
    }

    this.input = (TaskEditorInput) input;
    setSite(site);
    setInput(input);
    todo = TaskService.getInstance().getTaskById(this.input.getId());
    setPartName("Todo ID: " + todo.getId());
  }

  @Override
  public void createPartControl(Composite parent) {
    GridLayout layout = new GridLayout();
    layout.numColumns = 2;
    parent.setLayout(layout);
    new Label(parent, SWT.NONE).setText("Summary");
    Text text = new Text(parent, SWT.BORDER);
    text.setText(todo.getSummary());
    text.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
    
    new Label(parent, SWT.NONE).setText("Description");
    Text lastName = new Text(parent, SWT.BORDER);
    lastName.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
    lastName.setText(todo.getDescription());
    
    new Label(parent, SWT.NONE).setText("Done");
    Button doneBtn = new Button(parent, SWT.CHECK);
    doneBtn.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
    doneBtn.setSelection(todo.isDone());
    
    new Label(parent, SWT.NONE).setText("Due Date");
    DateTime dueDate = new DateTime(parent, SWT.CHECK);
    dueDate.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
    Date date = todo.getDueDate();
    dueDate.setDate(date.getYear(), date.getMonth(), date.getDay());
  }

  @Override
  public void doSave(IProgressMonitor monitor) {
  }

  @Override
  public void doSaveAs() {
  }

  @Override
  public boolean isDirty() {
    return false;
  }

  @Override
  public boolean isSaveAsAllowed() {
    return false;
  }

  @Override
  public void setFocus() {
  }

} 

3.4. Command for opening the editor

Create a command "com.vogella.rcp.editor.example.openEditor" with the default handler "com.vogella.rcp.editor.example.handler.CallEditor".

Create the following class "com.vogella.rcp.editor.example.handler.CallEditor".

package com.vogella.rcp.editor.example.handler;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.handlers.HandlerUtil;

import com.vogella.rcp.editor.example.editor.TaskEditor;
import com.vogella.rcp.editor.example.editor.TaskEditorInput;
import com.vogella.rcp.editor.example.model.Task;

public class CallEditor extends AbstractHandler {

  @Override
  public Object execute(ExecutionEvent event) throws ExecutionException {
    // get the page
    IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event);
    IWorkbenchPage page = window.getActivePage();
    // get the selection
    ISelection selection = HandlerUtil.getCurrentSelection(event);
    if (selection != null && selection instanceof IStructuredSelection) {
      Object obj = ((IStructuredSelection) selection).getFirstElement();
      // if we had a selection lets open the editor
      if (obj != null) {
        Task todo = (Task) obj;
        TaskEditorInput input = new TaskEditorInput(todo.getId());
        try {
          page.openEditor(input, TaskEditor.ID);
        } catch (PartInitException e) {
          throw new RuntimeException(e);
        }
      }
    }
    return null;
  }
} 

3.5. Task overview part

We will create the class TaskOverview to use JFace Viewers and add a double-click listener to call the command. The view makes his viewer available as selection provider via the following line:" getSite().setSelectionProvider(viewer);". This make is possible for the command which opens the editor to get the selection of the view. All workbench parts have a site, which can be accessed via the method getSite(). A site is a Facade which allows access to other parts of the workbench, e.g. the shell, the workbench window, etc. Whenever possible use the site to access Workbench objects.

package com.vogella.rcp.editor.example.parts;

import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.part.ViewPart;

import com.vogella.rcp.editor.example.model.Task;
import com.vogella.rcp.editor.example.model.TaskService;

public class TaskOverview extends ViewPart {
  public static final String ID = "com.vogella.rcp.editor.example.taskoverview";

  private ListViewer viewer;

  @Override
  public void createPartControl(Composite parent) {
    viewer = new ListViewer(parent);
    viewer.setContentProvider(ArrayContentProvider.getInstance());
    viewer.setLabelProvider(new LabelProvider() {
      @Override
      public String getText(Object element) {
        Task p = (Task) element;
        return p.getSummary();
      };
    });
    viewer.setInput(TaskService.getInstance().getTasks());
    getSite().setSelectionProvider(viewer);
    hookDoubleClickCommand();

  }

  private void hookDoubleClickCommand() {
    viewer.addDoubleClickListener(new IDoubleClickListener() {
      @Override
      public void doubleClick(DoubleClickEvent event) {
        IHandlerService handlerService = getSite().getService(IHandlerService.class);
        try {
          handlerService.executeCommand("com.vogella.rcp.editor.example.openEditor", null);
        } catch (Exception ex) {
          throw new RuntimeException(ex.getMessage());
        }
      }
    });
  }

  @Override
  public void setFocus() {
    viewer.getControl().setFocus();
  }
} 

Add the org.eclipse.ui.views extension point to the plugin.xml and configure the com.vogella.rcp.editor.example.taskoverview like this:

3.6. Validate

Run the Eclipse IDE together with your plugin and open the Task Overview part with the Show View menu or the Quick Access(Ctrl + 3).

If you double-click on an item in the view, the editor should opened in the editor area of your Eclipse IDE.

To participate in the global save command, set the isDirty property in your editor and call firePropertyChange(IEditorPart.PROP_DIRTY); to notify the workbench. I leave this as an exercise to the reader.

4. Texteditors and SourceViewer

Comes soon...

5. Exercise: Create a TextEditor with content assist

We could implement an editor like before and place a Text or even a StyledText control on it, but Eclipse offers a lot more than this, which you can use and extend. Things like ContentAssist, Quickfixes, Markers and many more.

So let us create a text editor for our Task model objects and reuse our com.vogella.rcp.editor.example plugin project.

5.1. Add new dependencies

Now that we want to implement an enhanced editor, we also need org.eclipse.ui.editors and org.eclipse.jface.text as plugin dependency, besides org.eclipse.ui and org.eclipse.core.runtime in our MANIFEST.MF.

5.2. Add Task Text Editor

A Text Editor is added to the plugin.xml in the same way, as a usual Editor. But we also add the task extension, so that our Task Text Editor is directly associated with *.task files.

The id of the editor should be com.vogella.rcp.editor.example.editor.tasktexteditor and the editor class will be called com.vogella.rcp.editor.example.editor.text.TaskTextEditor.

The difference here is that we do not extend EditorPart directly, but the TextEditor class.

The TextEditor class is a default implementation so that you are not forced to override any methods right now.

5.3. Add simple content assist

In other words we define a custom SourceViewerConfiguration for our TaskTextEditor, where we configure a simple example for content assist.

At first we create a TaskSourceViewerConfiguration, which extends TextSourceViewerConfiguration and place it in the com.vogella.rcp.editor.example.editor.text.config package.

package com.vogella.rcp.editor.example.editor.text.config;

import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistant;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;

public class TaskSourceViewerConfiguration extends TextSourceViewerConfiguration {

  
  private ContentAssistant contentAssistant;

  public TaskSourceViewerConfiguration() {
    this(null);
  }

  public TaskSourceViewerConfiguration(IPreferenceStore preferenceStore) {
    super(preferenceStore);
    
    // Initialize ContentAssistant
    contentAssistant = new ContentAssistant();
    
    // define a default ContentAssistProcessor
    contentAssistant.setContentAssistProcessor (new TaskCompletionProcessor(), 
        IDocument.DEFAULT_CONTENT_TYPE);
    
    // enable auto activation
    contentAssistant.enableAutoActivation(true);
    
    // set a proper orientation for the content assist proposal
    contentAssistant.setContextInformationPopupOrientation(IContentAssistant.CONTEXT_INFO_ABOVE);
  }

  @Override
  public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
    
    contentAssistant.setInformationControlCreator(getInformationControlCreator(sourceViewer));
    
    return contentAssistant;
  }
} 

Here we configure a ContentAssistant so that it can be returned by the getContentAssistant(ISourceViewer sourceViewer) method.

Now we need to implement the TaskCompletionProcessor, which is used in the TaskSourceViewerConfiguration.

Therefore we implement computeCompletionProposals(ITextViewer viewer, int offset) of the IContentAssistProcessor interface.

package com.vogella.rcp.editor.example.editor.text.config;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;

public class TaskCompletionProcessor implements IContentAssistProcessor {

  private String[] proposals = new String[] { "ID:", "Summary:", "Description:", "Done:", "Duedate:", "Dependent:" };

  @Override
  public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {

    IDocument document = viewer.getDocument();

    try {
      int lineOfOffset = document.getLineOfOffset(offset);
      int lineOffset = document.getLineOffset(lineOfOffset);

      // do not show any content assist in case the offset is not at the
      // beginning of a line
      if (offset != lineOffset) {
        return new ICompletionProposal[0];
      }
    } catch (BadLocationException e) {
      // ignore here and just continue
    }

    List<ICompletionProposal> completionProposals = new ArrayList<ICompletionProposal>();

    for (String c : proposals) {
      // Only add proposal if it is not already present
      if (!(viewer.getDocument().get().contains(c))) {
        completionProposals.add(new CompletionProposal(c, offset, 0, c.length()));
      }
    }

    return completionProposals.toArray(new ICompletionProposal[completionProposals.size()]);
  }

  @Override
  public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
    return null;
  }

  @Override
  public char[] getCompletionProposalAutoActivationCharacters() {
    return null;
  }

  @Override
  public char[] getContextInformationAutoActivationCharacters() {
    return null;
  }

  @Override
  public String getErrorMessage() {
    return null;
  }

  @Override
  public IContextInformationValidator getContextInformationValidator() {
    return null;
  }

} 

We also applied some rules for the content assist, so that it is a little bit context sensitive and only allows completion proposals at the beginning of a line and only those proposals, which are not already present in the document.

Please feel free to add your own rules here.

To apply this TaskSourceViewerConfiguration to our TaskTextEditor, we set it in the constructor of the TaskTextEditor, like this:

package com.vogella.rcp.editor.example.editor.text;

import org.eclipse.ui.editors.text.TextEditor;

import com.vogella.rcp.editor.example.editor.text.config.TaskSourceViewerConfiguration;

public class TaskTextEditor extends TextEditor {

  public static final String ID = "com.vogella.rcp.editor.example.editor.tasktexteditor";
  
  public TaskTextEditor() {
    System.out.println("Task Text Editor opened");
    setSourceViewerConfiguration(new TaskSourceViewerConfiguration());
  }

} 

5.4. Validate

Please start the IDE together with your plugin and create a project in the project explorer, where you create a example.task file. When you double klick on this file, the TaskTextEditor should be opened.

6. About this website

6.1. Donate to support free tutorials

Please consider a contribution Support free tutorials if this article helped you. It will help to maintain our content and our Open Source activities.

6.2. Questions and discussion

Writing and updating these tutorials is a lot of work. If this free community service was helpful, you can support the cause by giving a tip as well as reporting typos and factual errors.

If you find errors in this tutorial, please notify me (see the top of the page). Please note that due to the high volume of feedback I receive, I cannot answer questions to your implementation. Ensure you have read the vogella FAQ as I don't respond to questions already answered there.

6.3. License for this tutorial and its code

This tutorial is Open Content under the CC BY-NC-SA 3.0 DE license. Source code in this tutorial is distributed under the Eclipse Public License. See the vogella License page for details on the terms of reuse.

7. Links and Literature

7.1. Source Code

Source Code of Examples

7.2. Eclipse Resources

JFace Data Binding

7.3. vogella Resources

TRAINING SERVICE & SUPPORT
The vogella company provides comprehensive training and education services from experts in the areas of Eclipse RCP, Android, Git, Java, Gradle and Spring. We offer both public and inhouse training. Whichever course you decide to take, you are guaranteed to experience what many before you refer to as “The best IT class I have ever attended”. The vogella company offers expert consulting services, development support and coaching. Our customers range from Fortune 100 corporations to individual developers.