Java/RCP&GEF

Eclipse 4 RCP 정리

기은P 2020. 9. 9. 17:13
반응형

Eclipse 4 RCP 정리

 

 

 

1. Eclipse e4 install

 

https://download.eclipse.org/e4/snapshots/org.eclipse.e4.tools/latest/

Help – Install new software를 통해 위의 URL을 입력하고 tool들을 모두 체크한 다음 설치를 해주면 됩니다.

이클립스 최신 버전을 설치했을 경우 e4가 자동적으로 설치가 되어있을 수 있지만, Spy와 관련된 기능들은 설치가 되어 있지 않을 수가 있어 설치를 진행해줍니다.(현재 이클립스 버전 2020-06 (4.16.0))

 

 

 

 

2. Create Project

 

기존 RCP와 유사하게 Plug-in Project로 프로젝트를 생성합니다.

 

 

이클립스 버전은 3.5이상으로 설정해주고,

 

이용할 플러그인 템플릿을 Eclipse 4 RCP Application을 선택하면 됩니다.

 

 

 

마지막으로 create sample content에 체크를 해주는데, 기본적인 partmodel, menu, command, handler RCP 4에서 지원하는 새로운 기능들을 확인하기 위해서 체크를 해주는 것이 좋습니다. 그리고 finish를 해주면

 

 

이와 같은 프로젝트에 파일들이 자동적으로 생성이 됩니다.

 

 

 

3. Parts

 

파트(Part)들은 사용자 인터페이스 컴포넌트로서 우리는 파트를 통해서 데이터를 수정하거나 돌아다닐 수 있다. 파트들은 스택처럼 서로 겹쳐 쌓일수도 있고, 컨테이너에 담길수 도 있으며, 이 컨테이너에 의해서 서로 옆으로 나란히 위치시킬 수도 있다. 하나의 파트는 드랍다운 메뉴와 컨텍스메뉴 그리고 툴바를 가질 수 도 있다.

 

예에서 사용된 파트는 Sample Part, e4xmiApplication – Windows – Controls에서 Part를 정의할 수 있다.

 

 

package gef5test.parts;

import java.util.Arrays;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.eclipse.e4.ui.di.Focus;
import org.eclipse.e4.ui.di.Persist;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;

public class SamplePart {

	private TableViewer tableViewer;

	@Inject
	private MPart part;

	@PostConstruct
	public void createComposite(Composite parent) {
		parent.setLayout(new GridLayout(1, false));

		Text txtInput = new Text(parent, SWT.BORDER);
		txtInput.setMessage("Enter text to mark part as dirty");
		txtInput.addModifyListener(e -> part.setDirty(true));
		txtInput.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		tableViewer = new TableViewer(parent);

		tableViewer.setContentProvider(ArrayContentProvider.getInstance());
		tableViewer.setInput(createInitialDataModel());
		tableViewer.getTable().setLayoutData(new GridData(GridData.FILL_BOTH));
	}

 

 

 

 

3-1. Model Resources

모델 요소들은 URI(Uniform Resource Identifier) 통한 정적 리소스나 클래스를 연결 시킬 수 있다. 이렇게 하기 위해 이클립스는 두가지 URI 패턴을 정의한다. 이클립스는 대부분 참조하고 있는 객체들이나 리소스들을 늦게 초기화 한다. 예를들어 어떤 파트에 연결되어 있는 클래스는 파트가 실제 보이게 될 때 초기화 된다.

 

위의 예제의 Sample Partbundleclass://gef5Test/gef5test.parts.SamplePart 라는 Uri로 정의되어 있다. 이때 “bundleclass://” 부분은 고정 값이며, “gef5Test”는 프로젝트 네임, “gef5test.parts.”는 패키지명이다.

 

예를들어  Class URI 속성을 갖는 어떤 파트는 그 속성에 "bundleclass://URI" 방식으로 벨류를 입력하여 Java 클래스를 연결할 수 있다. 이 클래스는 파트의 행위를 제공하게 된다. 이 연결된 객체는 이클립스 프레임웍이 생성시킨다. 이전 설명에서 집/방 을 개념을 이용해서 은유적으로 설명했던 부분을 떠올려 보면, 이 클래스는 가구배치와 방의 레이아웃을 정의할 책임을 갖으며, 사용자와 상호작용 할 객체들이 어떻게 동작해야할지 정의 할 책임또한 갖는다.

 

추가적으로 Part 클래스들은 다른 클래스를 확장해서는 안되며, 어떠한 인터페이스도 구현해서는 안된다.

 

 

 

 

 

 

4. Available model objects

 

Application.e4xmi 파일에 저장되어 있는 사용자 변경 사항 및 모델에 기여하는 것들과 같은 애플리케이션 모델에 관한 정보를 파싱하며 이와 같은 정보들을 실행하는 동안에 자바 객체에 저장해둔다. 이 객체 들을 모델 객체라고 부르며, 실행하는 동안 모델 요소들의 속성들을 표시합니다.

 

모델 객체 유형 리스트

Model element

Description

MApplication

애플리케이션 객체를 설명한다. 다른 요소들 모두 이 객체에 포함된다.

MAddon

보통 사용자 인터페이스가 아닌 독립적인 컴포넌트. 애플리케이션 라이프 사이클내에서 발생하는 이벤트들을 등록하고 이 이벤트들을 처리할 수 있다.

MWindow

애플리케이션에 하나의 윈도우를 나타낸다.

MTrimmedWindow

MWindow 와 유사하지만, TrimBars 모델 요소를 이용하여 윈도우에 툴바를 포함할 수 있다.

MPerspective

윈도우내에 표시될 파트들이 용도에 따라 서로다르게 배치 되도록 할 수 있는 요소다. MPerspectiveStack 내에 있어야 한다.

MPart

뷰나 에디터 같은 파트 요소다.

MDrityable

주입될 수 있는 MPart 의 속성이다. true 로 설정하면, 이 속성은 이클립스 플랫폼에게 이 파트가 저장되지 않은 데이터를 포함하고 있다고 알리게 된다.(is dirty). 핸들러에서 우리는 저장이 필요한지를 확인하기 위해 이 속성을 확인할 수 있다.

MPartDescriptor

MPartDescriptor 는 새 파트를 만들기 위한 템플릿이다. 이클립스 프레임워크는 이 파트 디스크립터 정보를 가지고 새 파트를 만들고 디스플레이 할 수 있다.

Snippets

Snippets 은 우리가 프로그램을 통해 생성하고 싶은 모델 파트들을 미리 구성하는데 사용될 수 있다. 우리는 하나의 snippet 을 복사하고 그것을 실행시간에 애플리케이션 모델에 추가하기 위해 이클립스 프레임워크를 사용할 수 있다.

 

 

 

5. SWT Browser widget

 

RCP 4에서는 Part ClassSWT를 사용할 수 있다.

 

package com.example.e4.rcp.todo.parts;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import javax.annotation.PostConstruct;

import org.eclipse.e4.ui.di.Focus;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Text;

public class PlaygroundPart {
    private Text text;
    private Browser browser;

    @PostConstruct
    public void createControls(Composite parent) {
        parent.setLayout(new GridLayout(2, false));

        text = new Text(parent, SWT.BORDER);
        text.setMessage("Enter City");
        text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

        Button button = new Button(parent, SWT.PUSH);
        button.setText("Search");
        button.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                String city = text.getText();
                if (city.isEmpty()) {
                    return;
                }
                try {
                    // not supported at the moment by Google
                    // browser.setUrl("http://maps.google.com/maps?q="
                    // + URLEncoder.encode(city, "UTF-8")
                    // + "&output=embed");
                    browser.setUrl("https://www.google.com/maps/place/"
                            + URLEncoder.encode(city, "UTF-8")
                            + "/&output=embed");

                } catch (UnsupportedEncodingException e1) {
                    e1.printStackTrace();
                }
            }
        });

        browser = new Browser(parent, SWT.NONE);
        browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));

    }

    @Focus
    public void onFocus() {
        text.setFocus();
    }
}

 

위는 구글 맵 API를 사용하는 코드로 PlaygroundPartApplication.e4xmiWindows에서 참조하여 사용할 수 있다.

 

 

 

 

6. Dependency injection

 

이클립스에서 프로그래밍할 때 사용되는 모델은 생성자, 메소드, 필드 주입을 지원하는데, 이것은 Java Specification Request 330 (JSR 330)을 따르고 있다.

 

이에 따라 이클립스에서는 의존성 주입을 목적으로 하는 어노테이션을 정의한다.

 

따라서 이클립스 종속성 프레임워크는 주입되는 객체의 키와 타입이 정확한지를 확인한다.

 

예를 들어, @Inject @Named(“xyz”) Todo todo; 라는 코드가 있다면 xyz키에 해당하는 Todo 타입의 객체를 사용하고 싶다는 의미로 프레임워크에서는 할당할 수 있는 타입을 찾았을 경우에 하나의 객체만 주입한다는 의미이다.

 

Injection에 대해서는 아래 링크를 참고

https://poqw.github.io/di_1/

https://webcoding.tistory.com/entry/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-Autowired-Resource-Inject%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9D%98%EC%A1%B4-%EC%9E%90%EB%8F%99%EC%A3%BC%EC%9E%85

 

Annotation

Description

@javax.inject.Inject

JSR330 에서 정의하고 있으며, 필드, 생성자, 메소드에 추가될 수 있다. 이클립스 프레임워크는 필드나 파라미터 인스턴스들을 해당하는 객체에 주입한다.

@javax.inject.Named

JSR330 에서 정의하고 있으며, 값을 주입하기 위한 키를 정의한다. 기본적으로, 클래스 풀네임이(qualified class name) 키로 사용된다. 기본값을 위한 여러 키들이 IServiceConstants interface 에 상수로 정의되어 있다.

@Optional

이클립스 애노테이션은(Eclipse specific annotation) 주입되는 값을 옵셔널로 표시한다. 주어진 키에(또는 타입) 대한 유효한 값이 없을 경우라도 프레임워크는 예외를 던지지 않는다

구현 동작은  @Optional 이 위치한 곳에 의존적이다. 아래의 설명은 키를 기준으로 한것으로, 키가 결정될 수 없으면, 다음과 같은 일이 발생한다

1.
파라미터에 대해서는 null 값이 주입될 것이다
2.
메소드에 대해서는 호출되지 않고 넘어갈 것이다
3.
필드들에 대해서는 값들이 주입되지 않을 것이다

null
context 설정될 수 있는 값으로, context 에서 제거되는 키와는 다르다. 예를들어, context.set(SOMEKEY, null) 이 호출되면, SOMKEY 를 리스닝하고 있는 어떤것이라도 null을 주입할 것이다.

@GroupUpdates

이클립스 애노테이션은 (Eclipse specific annotation) @Inject 가 일괄 실행되기 위한 업데이트를 표시한다. 우리가 이클립스 컨텍스트 상에서 이러한 객체들을 변경하면,  IEclipseContext 객체의 processWaiting() 메소드는  update 를 시작시킨다. 이 에노테이션은 퍼포먼스 최적화를 위해 플랫폼이 사용하는 것으로 만들어 졌으며, 드믈게 RCP 애플리케이션들에서느 필요할 것이다.

 

 

7. Eclipse Context Life Cycle

이클립스 애플리케이션이 시작되는 동안 이클립스 런타임은 IEclipseContext interace 를 구현하는  하나의 객체를 생성한다. 이 객체를 context 또는 Eclipse context 라고 부른다.

 

context는 객체들이 특정 키 아래에 위치될 수 있다는 점에서  Map 데이터 구조와 유사하다. 키는 하나의 String 이며, 많은 경우에 풀네임의 클래스 명이 키로 사용된다. 키가 가리키는 값은 다른 객체들안으로 주입될 수 있다. 하지만 map 과 달리, 이클립스 context 는 계층적이며 또한 요청했던 키에 대한 값을 동적으로 계산할 수 있다.

 

다른 context 객체들은 우리의 애플리케이션 모델 구조를 기반으로 계층적인 트리 구조를 구성하기 위해 연결된다. 이 계층에서 가장 높은 레벨은 application context이다.

 

context hierarchy

 

 

 

 

8. Active Part or Shell

 

이클립스 플랫폼은 현재 선택된 파트와 애플리케이션 객체의 IEclipseContext 안으로 액티브 셸을 위치시킨다. 관련되는 키들은 IServiceConstants interface 에 정의된다.

 

@Inject
@Optional
public void receiveActivePart(
    @Named(IServiceConstants.ACTIVE_PART) MPart activePart) {
    if (activePart != null) {
        System.out.println("Active part changed "
                + activePart.getLabel());
    }
}

 

active shell 을 추적하기 위해서는  IServiceConstants.ACTIVE_SHELL 키를 사용한다.

 

// tracks the active shell
@Inject
@Optional
public void receiveActiveShell(
    @Named(IServiceConstants.ACTIVE_SHELL) Shell shell) {
    if (shell != null) {
        System.out.println("Active shell (Window) changed");
    }
}

 

 

 

9. Eclipse life cycle annotations for parts

Annotation

Description

@PostConstruct

클래스가 구성되고, 맴버변수와 메소드 인젝션이 수행된 후에 호출된다.

@PreDestroy

클래스가 소멸되기 전에 호출된다. 리소스들을 해제하는데 사용될 수 있다.

@Focus

파트가 포커싱 될 때마다 호출된다.

@Persist

이클립스 프레임워크가 파트에 저장요청을 하게 되면 호출된다.

@PersistState

파트가 인스턴스상태를 저장할수 있도록,  모델객체가 해제되기 전에 호출된다.  이 메소드는 @PreDestroy 메소드가 호출되기전에 호출된다.

 

RCP 4에서는 파트의 사용자 인터페이스, UI를 구성하는 Composite가 포함된 CreateControls @PostConstruct 애노테이션을 붙인 메소드에서 구성하는 것을 추천한다고 한다.

 

import javax.annotation.PostConstruct;
import org.eclipse.swt.widgets.Composite;

// more code

@PostConstruct
public void createControls(Composite parent) {
    System.out.println(this.getClass().getSimpleName()
    + " @PostConstruct method called.");
}

 

 

 

 

10. HandlerCommands의 사용

 

하나의 커맨드에는 save, edit, copy 와 같은기능을 수행하기 위한 액션들이 정의된다.

 

이런 커맨드의 행위는 Handler를 이용해서 정의한다. Handler 모델 요소는 contributionURI 속성을 이용해서 연결되는 클래스를 가리킨다. 이 속성은 모델 에디터 창에서 Class URL로 표시된다.

 

핸들러 클래스 내에서는 하나의 메소드에만 @Execute를 사용할 수 있고, 다른 하나의 메소드에만 @CanExecute를 사용할 수 있다.

 

Annotation

Description

@Execute

핸들러에서 액션을 책임질 메소드에 표시한다프레임워크는 사용자 인터페이스와 관련되는 매뉴 엔트리 등이 선택될 경우  메소드를 한번 실행시킨다.

@CanExecute

이클립스 프레임워크가 핸들러가 현재 실행 가능한지를 체크하기 위해 사용할 메소드에 표시해라핸들러 클래스의  메소드가 false  반환하면 이클립스는 해당하는 사용자 인터페이스를 비활성화 시킨다예를들어 핸들러 클래스의 @CanExecute  표시된 메소드에서 true  반환하면 저장 버튼은 활성화 된다 메소드의 기본 반환값은 true 이기 때문에 핸들러 클래스는 항상 실행될  있다. @CanExecute 메소드는 반드시 구현할 필요는 없다.

 

package com.example.e4.rcp.todo.handlers;

// import statements cut out
// ..

public class ExitHandler {
    @Execute
    public void execute(IWorkbench workbench) {
        workbench.close();
    }

    // NOT REQUIRED IN THIS EXAMPLE
    // just to demonstrates the usage of
    // the annotation
    @CanExecute
    public boolean canExecute() {
        return true;
    }

}

 

 

11. default ids for commonly used commands

Command

ID

Save

org.eclipse.ui.file.save

Save All

org.eclipse.ui.file.saveAll

Undo

org.eclipse.ui.edit.undo

Redo

org.eclipse.ui.edit.redo

Cut

org.eclipse.ui.edit.cut

Copy

org.eclipse.ui.edit.copy

Paste

org.eclipse.ui.edit.paste

Delete

org.eclipse.ui.edit.delete

Import

org.eclipse.ui.file.import

Export

org.eclipse.ui.file.export

Select All

org.eclipse.ui.edit.selectAll

About

org.eclipse.ui.help.aboutAction

Preferences

org.eclipse.ui.window.preferences

Exit

org.eclipse.ui.file.exit

 

 

 

 

 

 

반응형