Jump to: navigation, search

Difference between revisions of "Platform Command Framework/ko"

(아키텍처 개요)
(아키텍처 개요)
Line 1: Line 1:
 
= 아키텍처 개요 =
 
= 아키텍처 개요 =
  
[[Image:http://dev.eclipse.org/viewcvs/index.cgi/~checkout~/platform-ui-home/R3_1/contributions-proposal/requestForComments_html_m41374bdb.png]]
+
[[Image: http://dev.eclipse.org/viewcvs/index.cgi/~checkout~/platform-ui-home/R3_1/contributions-proposal/requestForComments_html_m41374bdb.png]]
  
 
<center>참고 1: 고수준 아키텍쳐</center>
 
<center>참고 1: 고수준 아키텍쳐</center>

Revision as of 04:16, 11 April 2008

아키텍처 개요

File:Http://dev.eclipse.org/viewcvs/index.cgi/~checkout~/platform-ui-home/R3 1/contributions-proposal/requestForComments html m41374bdb.png

참고 1: 고수준 아키텍쳐

Commands

Command들은 org.eclipse.ui.commands확장점과 ICommandService에 의해 관리된다.

확장점을 이용하여 Command를 만드는 예제:

<extension
      point="org.eclipse.ui.commands">
   <category
         description="Actions take at lunch time."
         id="z.ex.view.keybindings.category"
         name="Lunch">
   </category>
   <command
         categoryId="z.ex.view.keybindings.category"
         description="Go for the taco."
         id="z.ex.view.keybindings.eatTaco"
         name="Eat That Taco">
   </command>
</extension>

프로그래밍을 통하여 Command를 만들 수도 있다.

ICommandService cmdService = (ICommandService) getSite().getService(
    ICommandService.class);
Category lunch = cmdService
    .getCategory("z.ex.view.keybindings.category");
if (!lunch.isDefined()) {
  lunch.define("Lunch", "Actions take at lunch time.");
}
Command eatTaco = cmdService
    .getCommand("z.ex.view.keybindings.eatTaco");
if (!eatTaco.isDefined()) {
  eatTaco.define("Eat That Taco", "Go for the taco.", lunch);
}

하지만 기억해 두어야 할 것은, 프로그래밍으로 Command를 정의할 경우 플러그인이 전혀 언로드 되지 않을 경우에 대비하여 Command들을 적절한 시점에 Unload할 책임을 지닌다.

또한 IAction과 마찬가지로 Command를 직접 실행할 수도 있다. 하지만 더 나은 환경을 얻기 위해 IHandler 서비스를 거쳐서 실행하는 것이 더 좋다. #Handlers를 참고하라.

파라미터를 가진 Command의 실행

Command가 파라미터를 가질 때, 파라미터의 타입을 특정하거나, 올바른 값들에 대해 정의할 수 있다. 다음의 예는 showView Command 이다:

 <command
     name="%command.showView.name"
     description="%command.showView.description"
     categoryId="org.eclipse.ui.category.views"
     id="org.eclipse.ui.views.showView"
     defaultHandler="org.eclipse.ui.handlers.ShowViewHandler">
   <commandParameter
       id="org.eclipse.ui.views.showView.viewId"
       name="%command.showView.viewIdParameter"
       values="org.eclipse.ui.internal.registry.ViewParameterValues" />
 </command>

이 Command를 실행하기 위해서, Parameterization객체(파라미터와 파라미터 값을 추상화 하는 인스턴스)를 이용하여 ParameterizedCommand를 만들어야한다.

		ICommandService commandService = ...;
		IHandlerService handlerService = ...;
		Command showView = commandService
				.getCommand("org.eclipse.ui.views.showView");
		IParameter viewIdParm = showView
				.getParameter("org.eclipse.ui.views.showView.viewId");
		
		// viewId 파라미터는 유효한 값들의 리스트를 제공한다.
		// 이미 원하는 뷰의 id를 알고 있다면, 이 과정을 생략해도 된다.
		// 이 메서드는 사용자가 원하는 값을 선택할 수 있도록
		// 키 설정 페이지 등에서 사용되는 목적으로 만들어졌다.
		IParameterValues parmValues = viewIdParm.getValues();
		String viewId = null;
		Iterator i = parmValues.getParameterValues().values().iterator();
		while (i.hasNext()) {
			String id = (String) i.next();
			if (id.indexOf("ProblemView") != -1) {
				viewId = id;
				break;
			}
		}

		Parameterization parm = new Parameterization(viewIdParm, viewId);
		ParameterizedCommand parmCommand = new ParameterizedCommand(
				showView, new Parameterization[] { parm });

		handlerService.executeCommand(parmCommand, null);


이 코드는 Id에 ProblemView 문자열을 가진 view 파트를 찾아내어 showView Command를 실행한다.

아래 코드는 선언적으로 커맨드에 파라미터를 주어 키 바인딩 할 때 이용된다:

 <key
     sequence="M2+M3+Q X"
     contextId="org.eclipse.ui.contexts.window"
     commandId="org.eclipse.ui.views.showView"
     schemeId="org.eclipse.ui.defaultAcceleratorConfiguration">
   <parameter 
       id="org.eclipse.ui.views.showView.viewId"
       value="org.eclipse.ui.views.ProblemView" />
 </key>

위임 액션(IActionDelegate)으로 Command 실행 하기

이클립스 3.1과 3.2에는 메뉴 아이템이 Command를 수행하도록 하는 선언 지원이 없었다. 하지만 IActionDelegate를 작성해서(GenericCommandActionDelegate와 유사하게) 표준 확장점들이 이것을 Command를 실행하는 용도로 사용하게 할 수 있다. (org.eclipse.ui.actionSets, org.eclipse.ui.popupMenus, org.eclipse.ui.editorActions, and org.eclipse.ui.viewActions)


커맨드를 메뉴 아이템과 연결하기위해 다음과 같은 것들이 필요하다: (역자주) 이 방법은 3.2 이하의 버전을 위한 지침이다.

  1. plugin.xml 마크업을 이용하는 확장점 안에 action를 선언한다.
  2. 이 액션과 IActionDelegate 인스턴스를 연결한다. (마찬가지로 plugin.xml 마크업으로)
  3. IActionDelegate 에게 어떤 Command를 실행해야 하는지 알려준다. (마찬가지로 plugin.xml 마크업으로)
  4. IActionDelegate 클래스가 Command를 수행하게 만든다.

윗절에서 showView 커맨드가 하나의 파라미터 - view id - 를 가지는 것을 보았다. 아래는 어떻게 이것과 마찬가지 일을 하는 Action을 작성할 수 있는지를 보여준다:

        <action
              id="org.eclipse.ui.examples.actions.showOutlineView"
              label="Show View:Outline"
              menubarPath="org.eclipse.ui.examples.actions.showViewMenu/additions"
              style="push">
           <class class="org.eclipse.ui.tests.api.GenericCommandActionDelegate">
              <parameter name="commandId" 
                         value="org.eclipse.ui.views.showView"/>
              <parameter name="org.eclipse.ui.views.showView.viewId" 
                         value="org.eclipse.ui.views.ContentOutline"/>
           </class>
        </action>
        <action
              id="org.eclipse.ui.examples.actions.showBookmarkView"
              label="Show View:Bookmark"
              menubarPath="org.eclipse.ui.examples.actions.showViewMenu/additions"
              style="push">
           <class class="org.eclipse.ui.tests.api.GenericCommandActionDelegate">
              <parameter name="commandId" 
                         value="org.eclipse.ui.views.showView"/>
              <parameter name="org.eclipse.ui.views.showView.viewId" 
                         value="org.eclipse.ui.views.BookmarkView"/>
           </class>
        </action>

노트:

  • 파라미터가 없는 Command의 경우, class 속성(attribute)에 다음과 같은 축약식을 기록할 수 있다:
class="org.eclipse.ui.tests.api.GenericCommandActionDelegate:my.commandId"
  • 이런 액션 선언은 키 바인딩 선언과도 유사하다. 커맨드 아이디와 액션에 필요한 어떤 파라미터도 특정할 수 있다.
  • We specifed the action using the <class/> element instead of the class attribute ... you'll get a couple of warnings, ignore them.
  • "class" Attribute 대신에 <class/> 엘리먼트를 이용할 수 있다(warning이 약간 나지만 무시해♥)
  • 현재 <action/> 엘리먼틔 안에 있는 definitionId는 구형 action이 Command를 거쳐 키바인딩이 이루어지게 하기 위해 존재한다. 예를 들어 showMyViewActionDelegate 와 같은 구현을 3.0 이전에 작성해 두었을 수 있다. 이러한 경우를 위해 준비 되 있긴 하지만, 우리는 그렇게 쓰는 것을 별로 원하지 않는다.
  • 방금 보여준 마크업들은 단지 Action이 Command를 트리거 할 수 있게 하는 방법을 알려주기 위해서 였다. 애초에 Action이 menu에 삽입 될 수 있도록 디자인 되었기 때문에, 이러한 접근법으로 Command가 메뉴 아이템에 위해 트리거 되는 것을 허용한다. 액션과 Command 사이의 응집성은 IActionDeligate 구현의 일반적인 현상이다. (아래에서 볼수 있듯) 현재 모든 특정 동작 단위를 나타내는 코드들은 Command와 이것의 Handler로 통합되었다. 우리는 더 이상 run메소드를 다른 객체에게 위임하는 액션위임 객체를 메뉴 아이템을 위해 이용할 필요가 없다. 마찬가지로 더 이상 액션 셋에 있는 definitionId 속성을 Command의 Id를 지정하는데 써야될 이유도 없다.

알려진 이슈: 처음 이클립스가 시작될때 "class" attribute를 같지 않은 액션들에 대해 약간의 경고를 볼 수 있다. 이것은 에러가 아니며 퍼포먼스에 영향을 주지도 않는다.

또한 키바인딩 설정 페이지에서 Uncategorized Section을 살펴보면 action들을 볼수 있을 것이며, 키 설정도 할 수 있다. 실제로는 Action이 아니라 Command에 바인딩 되기는 하지만 어쨌든 되긴 된다.


Generic Command Action Delegate

일반적인 Command 액션 위임

이게 배포되고 있긴 해도 우린 좀더 로버스트한 구현이 필요하다. (역주)대충 주절 주절 SVN head에서 최신 버전 유지하라는 이야기. 3.4에서는 완전히 잘 작동한다. 아래는 코드:


/*******************************************************************************
 * Copyright (c) 2006 IBM Corporation and others.
 * 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.tests.api;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.IParameter;
import org.eclipse.core.commands.Parameterization;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExecutableExtension;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IViewActionDelegate;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.handlers.IHandlerService;

/**
 * This action delegate can be used to specify a command with or without
 * parameters be called from an <action/> specified in actionSets,
 * editorActions, viewActions, or popupMenus.
 */
public class GenericCommandActionDelegate implements
        IWorkbenchWindowActionDelegate, IViewActionDelegate,
        IEditorActionDelegate, IObjectActionDelegate, IExecutableExtension {

    private static final String PARM_COMMAND_ID = "commandId";

    private String commandId = null;

    private Map parameterMap = null;

    private ParameterizedCommand parameterizedCommand = null;

    private IHandlerService handlerService = null;

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#dispose()
     */
    public void dispose() {
        handlerService = null;
        parameterizedCommand = null;
        parameterMap = null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
     */
    public void run(IAction action) {
        if (handlerService == null) {
            // what, no handler service ... no problem
            return;
        }
        try {
            if (commandId != null) {
                handlerService.executeCommand(commandId, null);
            } else if (parameterizedCommand != null) {
                handlerService.executeCommand(parameterizedCommand, null);
            }
            // else there is no command for this delegate
        } catch (Exception e) {
            // exceptions reduced for brevity
            // and we won't just do a print out :-)
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction,
     *      org.eclipse.jface.viewers.ISelection)
     */
    public void selectionChanged(IAction action, ISelection selection) {
        // we don't care, handlers get their selection from the
        // ExecutionEvent application context
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement,
     *      java.lang.String, java.lang.Object)
     */
    public void setInitializationData(IConfigurationElement config,
            String propertyName, Object data) throws CoreException {
        String id = config.getAttribute(IWorkbenchRegistryConstants.ATT_ID);
        // save the data until our init(*) call, where we can get
        // the services.
        if (data instanceof String) {
            commandId = (String) data;
        } else if (data instanceof Map) {
            parameterMap = (Map) data;
            if (parameterMap.get(PARM_COMMAND_ID) == null) {
                Status status = new Status(IStatus.ERROR,
                        "org.eclipse.ui.tests", "The '" + id
                                + "' action won't work without a commandId");
                throw new CoreException(status);
            }
        } else {
            Status status = new Status(
                    IStatus.ERROR,
                    "org.eclipse.ui.tests",
                    "The '"
                            + id
                            + "' action won't work without some initialization parameters");
            throw new CoreException(status);
        }
    }

    /**
     * Build a command from the executable extension information.
     * 
     * @param commandService
     *            to get the Command object
     */
    private void createCommand(ICommandService commandService) {
        String id = (String) parameterMap.get(PARM_COMMAND_ID);
        if (id == null) {
            return;
        }
        if (parameterMap.size() == 1) {
            commandId = id;
            return;
        }
        try {
            Command cmd = commandService.getCommand(id);
            if (!cmd.isDefined()) {
                // command not defined? no problem ...
                return;
            }
            ArrayList parameters = new ArrayList();
            Iterator i = parameterMap.keySet().iterator();
            while (i.hasNext()) {
                String parmName = (String) i.next();
                if (PARM_COMMAND_ID.equals(parmName)) {
                    continue;
                }
                IParameter parm = cmd.getParameter(parmName);
                if (parm == null) {
                    // asking for a bogus parameter? No problem
                    return;
                }
                parameters.add(new Parameterization(parm, (String) parameterMap
                        .get(parmName)));
            }
            parameterizedCommand = new ParameterizedCommand(cmd,
                    (Parameterization[]) parameters
                            .toArray(new Parameterization[parameters.size()]));
        } catch (NotDefinedException e) {
            // command is bogus? No problem, we'll do nothing.
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#init(org.eclipse.ui.IWorkbenchWindow)
     */
    public void init(IWorkbenchWindow window) {
        if (handlerService != null) {
            // already initialized
            return;
        }

        handlerService = (IHandlerService) window
                .getService(IHandlerService.class);
        if (parameterMap != null) {
            ICommandService commandService = (ICommandService) window
                    .getService(ICommandService.class);
            createCommand(commandService);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IViewActionDelegate#init(org.eclipse.ui.IViewPart)
     */
    public void init(IViewPart view) {
        init(view.getSite().getWorkbenchWindow());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IEditorActionDelegate#setActiveEditor(org.eclipse.jface.action.IAction,
     *      org.eclipse.ui.IEditorPart)
     */
    public void setActiveEditor(IAction action, IEditorPart targetEditor) {
        // we don't actually care about the active editor, since that
        // information is in the ExecutionEvent application context
        // but we need to make sure we're initialized.
        if (targetEditor != null) {
            init(targetEditor.getSite().getWorkbenchWindow());
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.IObjectActionDelegate#setActivePart(org.eclipse.jface.action.IAction,
     *      org.eclipse.ui.IWorkbenchPart)
     */
    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
        // we don't actually care about the active part, since that
        // information is in the ExecutionEvent application context
        // but we need to make sure we're initialized.
        if (targetPart != null) {
            init(targetPart.getSite().getWorkbenchWindow());
        }
    }
}

핸들러

핸들러는 org.eclipse.ui.handlers 확장점과 IHandlerService에 의해 관리된다. 한 Command에 대해 여러개의 핸들러가 등록될 수 있다. 어느시점이건 0혹은 한개의 핸들러가 Command에 대응하여 활성화 된다. 하나의 핸들러의 활성 상태와 가용 상태는 선언적으로 다룰 수 있다. Command Core Expressions 에서 자세한 정보를 제공한다. 핸들러는 Command의 파라미터를 ExcutionEvent 로 부터 얻어 인터프리팅할 책임을 진다.

<extension
      point="org.eclipse.ui.handlers">
   <handler
         class="z.ex.view.keybindings.handlers.TacoHandler"
         commandId="z.ex.view.keybindings.eatTaco">
      <activeWhen>
         <with variable="activeContexts">
            <iterate operator="or">
               <equals value="z.ex.view.keybindings.contexts.taco"/>
            </iterate>
         </with>
      </activeWhen>
   </handler>
</extension>

아래 핸들러는 activeContexts 변수 (See org.eclipse.ui.ISources) 체크 하고 만약 "taco" 문맥이 활성상태라면, 핸들러가 활성화 됨을 의미한다.

TacoHandler 핸들러는 IHandler 구현해야 하고, 보통 추상 베이스 클래스를 상속받아 만든다.org.eclipse.core.commands.AbstractHandler.

또 핸들러 활성화를 프로그래밍 문맥을 통해 제어할 수도 있다:

IHandlerService handlerService = (IHandlerService) getSite()
    .getService(IHandlerService.class);
IHandler handler = new AbstractHandler() {
  public Object execute(ExecutionEvent event)
          throws ExecutionException {
    System.out.println("Eat that Taco");
    return null;
  }
};
handlerService
    .activateHandler("z.ex.view.keybindings.eatTaco", handler);


3.2 버전 이상에서 Command를 실행하기 위해서 IHandlerService를 불러야 한다. Command 객체를 스스로 excute()하게 해서는 안된다. (역자주) 일반적인 Command 패턴과 다른 점이니 조심!

handlerService.executeCommand("z.ex.view.keybindings.eatTaco", null);

3.1 에서 IHanderlService는 excuteCommand(*)를 지원하지 않는다. 하지만 직접 동일한 실행 환경을 제공할 수 있다.

Command eatTaco = cmdService
    .getCommand("z.ex.view.keybindings.eatTaco");
eatTaco.execute(new ExecutionEvent(Collections.EMPTY_MAP, null, handlerService.getCurrentState()));

키 바인딩

키 바인딩은 org.eclipse.ui.bindings 확장점을 통해 관리할 수 있다. 이는 "프로그래밍"으로 업데이트 할 수 없다.

<extension
      point="org.eclipse.ui.bindings">
   <key
         commandId="z.ex.view.keybindings.eatTaco"
         contextId="z.ex.view.keybindings.contexts.taco"
         schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
         sequence="CTRL+3">
   </key>
</extension>

키 바인딩은 컨텍스트가 활성화 될때 활성화 된다. 키 바인딩은 Command와 연결된다.(옵션으로 파라미터 id와 value들을 가질수도 있다) 만약 키가 눌렸을 때, 커맨드가 핸들러를 가지고 있다면 핸들러가 커맨드 파라미터들을 ExecutionEvent로 부터 얻어내어 특정한 동작을 취하게 한다.

컨텍스트

컨텍스트는 org.eclipse.ui.contexts 확장점과 IContextService에 의해 관리 된다. 대부분의 컨텍스트는 확장점 정의에 의해 만들어지고, 프로그래밍을 통해 적절한 시기에 활성화 된다. 일반적으로 활성 컨텍스트는 트리의 형태를 하고 있으며, 부모 문맥이 활성화 되면 자식도 함께 활성화 된다.

<extension
      point="org.eclipse.ui.contexts">
   <context
         description="To allow the consumption of Tacos"
         id="z.ex.view.keybindings.contexts.taco"
         name="Mexican Food"
         parentId="org.eclipse.ui.contexts.window">
   </context>
</extension>

컨텍스트가 어떤 view에 붙어 있다면, 대게 이 컨텍스트는 뷰의 createPartControl(*)에 의해서 활성화 된다.

IContextService contextService = (IContextService) getSite()
  .getService(IContextService.class);
IContextActivation contextActivation = contextService.activateContext("z.ex.view.keybindings.contexts.taco");

개발자는 활성화할 책임을 가진 컨텍스트 만을 비활성화 할 수 있다.

프로그래밍을 통해 컨텍스트를 만들 수도 있다:

Context tacos = contextService
    .getContext("z.ex.view.keybindings.contexts.taco");
if (!tacos.isDefined()) {
  tacos.define("Mexican Food", "To allow the consumption of Tacos",
      "org.eclipse.ui.contexts.window");
}

컨텍스트를 프로그래밍을 통해 생성하는 플러그인은 플러그인이 메모리에 계속 상주할경우 이러한 컨텍스트의 자원을 관리해야 할 책임을 진다.

Menu Contributions

참고 Menu Contributions

Tracing Option

키바인딩과 Command가 제대로 돌아가지 않는데는 몇가지 일반적인 이유가 있다.

  1. 키바인딩이 활성화 되지 않은 컨텍스트 안에 있을 때
  2. 키바인딩의 키 설정이 다른 키바인딩과 충돌이 있을 때
  3. 선택된 커맨들를 수행할 활성화된 핸들러가 없을 때
  4. 한 커맨드를 처리하려는 핸들러가 여러개일 때

이들을 추적하는 것을 돕기 위해 디버그 추적 옵션을 이용할 수 있다:

org.eclipse.ui/debug=true
org.eclipse.ui/trace/keyBindings=true
org.eclipse.ui/trace/keyBindings.verbose=true
org.eclipse.ui/trace/sources=true
org.eclipse.ui/trace/handlers=true
org.eclipse.ui/trace/handlers.verbose=true
#org.eclipse.ui/trace/handlers.verbose.commandId=org.eclipse.ui.edit.copy
org.eclipse.ui/trace/handlers.verbose.commandId=org.eclipse.jdt.ui.navigate.open.type
org.eclipse.ui/trace/contexts=true
org.eclipse.ui/trace/contexts.verbose=true


debug.options 파일에 상기내용을 기록하고 이클립스를 실행한다:

bash$ eclipse -debug debug.options -data /opt/local/pw_workspace >debug.log 2>&1

debug.log 파일에 디버그 기록이 남게 된다. 이 방법은 윈도우에서도 잘 된다:

C:\development> eclipse33\eclipsec.exe -debug debug.options -data workspaces\pw_workspace >debug.log 2>&1

handlers.verbose.commandId는 제대로 동작하지 않는 특정 커맨드를 추적할 수 있게 해 준다.

번역 피드백

papilla@tomatosystem.co.kr