/*******************************************************************************
 * Copyright (c) 2015, 2016 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 ******************************************************************************/

package org.eclipse.ui.internal.e4.migration;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.e4.ui.model.application.ui.MElementContainer;
import org.eclipse.e4.ui.model.application.ui.MUIElement;
import org.eclipse.e4.ui.model.application.ui.SideValue;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.model.application.ui.advanced.MPlaceholder;
import org.eclipse.e4.ui.model.application.ui.basic.MPartSashContainer;
import org.eclipse.e4.ui.model.application.ui.basic.MPartSashContainerElement;
import org.eclipse.e4.ui.model.application.ui.basic.MPartStack;
import org.eclipse.e4.ui.model.application.ui.basic.MStackElement;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow;
import org.eclipse.e4.ui.workbench.IPresentationEngine;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.IWorkbenchConstants;
import org.eclipse.ui.internal.PerspectiveTagger;
import org.eclipse.ui.internal.e4.compatibility.ModeledPageLayout;
import org.eclipse.ui.internal.e4.compatibility.ModeledPageLayoutUtils;
import org.eclipse.ui.internal.e4.migration.InfoReader.PageReader;
import org.eclipse.ui.internal.e4.migration.InfoReader.PartState;
import org.eclipse.ui.internal.e4.migration.PerspectiveReader.DetachedWindowReader;
import org.eclipse.ui.internal.e4.migration.PerspectiveReader.ViewLayoutReader;
import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
import org.eclipse.ui.internal.registry.RegistryReader;
import org.eclipse.ui.internal.registry.StickyViewDescriptor;

public class PerspectiveBuilder {

	static final String ORIGINAL_ID = "originalId"; //$NON-NLS-1$

	static final String BASE_PERSPECTIVE_ID = "basePerspectiveId"; //$NON-NLS-1$

	private static final String DEFAULT_FASTVIEW_STACK = "defaultFastViewStack"; //$NON-NLS-1$

	private static final String ID_EDITOR_AREA = IPageLayout.ID_EDITOR_AREA;

	@Inject
	private PerspectiveReader perspReader;

	@Inject
	private EModelService modelService;

	private MPerspective perspective;

	private List<String> tags;

	private List<String> renderedViews;

	private List<String> defaultFastViews;

	private Map<String, MPlaceholder> viewPlaceholders = new HashMap<>();

	private Map<String, ViewLayoutReader> viewLayouts;

	private MPlaceholder editorAreaPlaceholder;

	private ModeledPageLayoutUtils layoutUtils;

	private Integer defaultFastViewSide;

	@PostConstruct
	private void postConstruct() {
		layoutUtils = new ModeledPageLayoutUtils(modelService);
	}

	public MPerspective createPerspective() {
		create();
		tags = perspective.getTags();
		populate();
		return perspective;
	}

	public MPerspective createPerspective(Integer defaultFastViewSide) {
		this.defaultFastViewSide = defaultFastViewSide;
		return createPerspective();
	}

	private void create() {
		perspective = modelService.createModelElement(MPerspective.class);
		perspective.setElementId(perspReader.getId());
		String label = perspReader.getLabel();
		perspective.setLabel(label);
		perspective.setTooltip(label);
		if (perspReader.isCustom()) {
			perspective.getTransientData().put(BASE_PERSPECTIVE_ID, perspReader.getBasicPerspectiveId());
			perspective.getTransientData().put(ORIGINAL_ID, perspReader.getOriginalId());
		}
	}

	private void populate() {
		addActionSetTags();
		addPerspectiveShortcutTags();
		addNewWizardTags();
		addShowViewTags();
		addHiddenItems();
		addShowInTags();

		for (InfoReader info : perspReader.getInfos()) {
			if (info.isEditorArea()) {
				addEditorArea(info);
			} else if (info.isFolder()) {
				MPartStack stack = addPartStack(info);
				populatePartStack(stack, info);
			}
		}

		addDefaultFastViewStack();
		setZoomState();
		addDetachedWindows();
		hideEmptyStacks();
		hideUrenderableSashes();
		hideInvisibleSashes();
		processStandaloneViews();
		correctSelectedElements();
		addTrimBars();
		PerspectiveTagger.tagPerspective(perspective, modelService);
	}

	private void processStandaloneViews() {
		Map<String, ViewLayoutReader> viewLayouts = perspReader.getViewLayouts();
		for (Entry<String, ViewLayoutReader> entry : viewLayouts.entrySet()) {
			String viewId = entry.getKey();
			MPlaceholder placeholder = viewPlaceholders.get(viewId);
			if (placeholder == null) {
				continue;
			}
			if (entry.getValue().isStandalone()) {
				MElementContainer<MUIElement> parent = placeholder.getParent();
				placeholder.setContainerData(parent.getContainerData());
				parent.getChildren().remove(placeholder);
				MElementContainer<MUIElement> grandParent = parent.getParent();
				int location = grandParent.getChildren().indexOf(parent);
				grandParent.getChildren().add(location, placeholder);
				grandParent.getChildren().remove(parent);
			}
		}
	}

	private void addDetachedWindows() {
		for (DetachedWindowReader detachedWindowReader : perspReader.getDetachedWindows()) {
			MTrimmedWindow detachedWindow = modelService.createModelElement(MTrimmedWindow.class);
			Rectangle bounds = detachedWindowReader.getBounds();
			detachedWindow.setX(bounds.x);
			detachedWindow.setY(bounds.y);
			detachedWindow.setWidth(bounds.width);
			detachedWindow.setHeight(bounds.height);
			MPartStack stack = modelService.createModelElement(MPartStack.class);
			populatePartStack(stack, detachedWindowReader);
			detachedWindow.getChildren().add(stack);
			perspective.getWindows().add(detachedWindow);
		}
	}

	private void addTrimBars() {
		Map<String, Integer> fastViewBars = perspReader.getFastViewBars();
		if (fastViewBars.isEmpty() && defaultFastViews.isEmpty()) {
			return;
		}

		int topCounter = 0;
		int bottomCounter = 0;
		int rightCounter = 0;
		int leftCounter = 0;
		StringBuilder sb = new StringBuilder();

		if (defaultFastViews.size() > 0) {
			sb.append(DEFAULT_FASTVIEW_STACK).append(' ');
			if (defaultFastViewSide != null) {
				switch (defaultFastViewSide) {
				case SWT.TOP:
					sb.append(SideValue.TOP_VALUE).append(' ').append(topCounter++);
					break;
				case SWT.BOTTOM:
					sb.append(SideValue.BOTTOM_VALUE).append(' ').append(bottomCounter++);
					break;
				case SWT.RIGHT:
					sb.append(SideValue.RIGHT_VALUE).append(' ').append(rightCounter++);
					break;
				default:
					sb.append(SideValue.LEFT_VALUE).append(' ').append(leftCounter++);
					break;
				}
			} else {
				sb.append(SideValue.BOTTOM_VALUE).append(' ').append(bottomCounter++);
			}
			sb.append('#');
		}

		if (fastViewBars.size() > 0) {
			for (InfoReader folder : perspReader.getInfos()) {
				String folderId = folder.getId();
				if (!fastViewBars.containsKey(folderId)) {
					continue;
				}

				sb.append(folderId).append(' ');

				Integer side = fastViewBars.get(folderId);
				if (side == null) {
					side = SWT.LEFT;
				}

				switch (side) {
				case SWT.TOP:
					sb.append(SideValue.TOP_VALUE).append(' ').append(topCounter++);
					break;
				case SWT.BOTTOM:
					sb.append(SideValue.BOTTOM_VALUE).append(' ').append(bottomCounter++);
					break;
				case SWT.RIGHT:
					sb.append(SideValue.RIGHT_VALUE).append(' ').append(rightCounter++);
					break;
				default:
					sb.append(SideValue.LEFT_VALUE).append(' ').append(leftCounter++);
					break;
				}

				sb.append('#');
			}
		}
		perspective.getPersistedState().put("trims", sb.toString()); //$NON-NLS-1$
	}

	private void hideEmptyStacks() {
		for (MPartStack stack : modelService.findElements(perspective, null, MPartStack.class)) {
			if (ID_EDITOR_AREA.equals(stack.getElementId())
					|| ID_EDITOR_AREA.equals(stack.getParent().getElementId())) {
				continue;
			}
			if (!hasRenderableContent(stack)) {
				stack.setToBeRendered(false);
			}
		}
	}

	private void setZoomState() {
		List<MPartStack> stacks = modelService.findElements(perspective, null, MPartStack.class, null);
		boolean isAnythingMaximized = isMaximized(editorAreaPlaceholder) || isAnyMaximized(stacks);
		if (isAnythingMaximized) {
			markMinimizedByZoom(editorAreaPlaceholder);
			for (MPartStack stack : stacks) {
				markMinimizedByZoom(stack);
			}
		}
	}

	private void markMinimizedByZoom(MUIElement element) {
		List<String> tags = element.getTags();
		if (tags.contains(IPresentationEngine.MINIMIZED)) {
			tags.add(IPresentationEngine.MINIMIZED_BY_ZOOM);
		}
	}

	private boolean isAnyMaximized(List<MPartStack> stacks) {
		for (MPartStack stack : stacks) {
			if (isMaximized(stack)) {
				return true;
			}
		}
		return false;
	}

	private boolean isMaximized(MUIElement element) {
		return element.getTags().contains(IPresentationEngine.MAXIMIZED);
	}

	private void hideUrenderableSashes() {
		for (MPartSashContainer sash : modelService.findElements(perspective, null, MPartSashContainer.class)) {
			hideUnrenderableSash(sash);
		}
	}

	private void hideInvisibleSashes() {
		for (MPartSashContainer sash : modelService.findElements(perspective, null, MPartSashContainer.class, null)) {
			hideInvisibleSash(sash);
		}
	}

	private void hideInvisibleSash(MElementContainer<?> container) {
		if ((container instanceof MPartSashContainer) && container != perspective) {
			if (!hasVisibleContent((MPartSashContainer) container)) {
				container.setVisible(false);
				hideInvisibleSash(container.getParent());
			}
		}
	}

	private boolean hasVisibleContent(MPartSashContainer sash) {
		for (MPartSashContainerElement child : sash.getChildren()) {
			if (child.isVisible()) {
				return true;
			}
		}
		return false;
	}

	private void hideUnrenderableSash(MElementContainer<?> container) {
		if ((container instanceof MPartSashContainer) && container != perspective) {
			if (modelService.countRenderableChildren(container) == 0) {
				container.setToBeRendered(false);
				hideUnrenderableSash(container.getParent());
			}
		}
	}

	private boolean hasRenderableContent(MPartStack stack) {
		for (MStackElement child : stack.getChildren()) {
			if (child.isVisible() && child.isToBeRendered()) {
				return true;
			}
		}
		return false;
	}

	private void correctSelectedElements() {
		List<MPartSashContainerElement> perspChildren = perspective.getChildren();
		if (perspective.getSelectedElement() == null && !perspChildren.isEmpty()) {
			for (MPartSashContainerElement child : perspChildren) {
				if (child.isToBeRendered()) {
					perspective.setSelectedElement(child);
					break;
				}
			}
		}

		for (MPartSashContainerElement child : perspChildren) {
			correctSelectedElements(child);
		}
	}

	private void correctSelectedElements(MUIElement element) {
		if (!(element instanceof MPartSashContainer || element instanceof MPartStack)) {
			return;
		}
		@SuppressWarnings("unchecked")
		MElementContainer<MUIElement> container = (MElementContainer<MUIElement>) element;
		List<MUIElement> children = container.getChildren();
		if (container.getSelectedElement() == null && !children.isEmpty()) {
			MUIElement firstRenderableElement = getFirstRenderableElement(children);
			if (firstRenderableElement != null) {
				container.setSelectedElement(firstRenderableElement);
			}
		}
		for (MUIElement child : children) {
			correctSelectedElements(child);
		}
	}

	private MUIElement getFirstRenderableElement(List<MUIElement> elements) {
		for (MUIElement element : elements) {
			if (element.isToBeRendered()) {
				return element;
			}
		}
		return null;
	}

	private void addToPerspective(MPartSashContainerElement element, InfoReader info) {
		if (info.isRelativelyPositioned()) {
			insert(element, info);
		} else {
			perspective.getChildren().add(element);
		}
	}

	private void addEditorArea(InfoReader info) {
		editorAreaPlaceholder = modelService.createModelElement(MPlaceholder.class);
		editorAreaPlaceholder.setElementId(ID_EDITOR_AREA);
		editorAreaPlaceholder.setToBeRendered(perspReader.isEditorAreaVisible());
		setPartState(editorAreaPlaceholder, perspReader.getEditorAreaState());
		addToPerspective(editorAreaPlaceholder, info);
	}

	private MPartStack addPartStack(InfoReader info) {
		MPartStack stack = createPartStack(info);
		if (info.isRelativelyPositioned()) {
			String refElementId = info.getRelative();
			MUIElement refElement = modelService.find(refElementId, perspective);
			MElementContainer<?> parent = refElement.getParent();
			// don't put a stack in another stack
			if (parent instanceof MPartStack) {
				refElement = parent;
			}

			insert(stack, refElement, info);
		} else {
			perspective.getChildren().add(stack);
		}
		setPartState(stack, info.getState());
		return stack;
	}

	private MPartStack addDefaultFastViewStack() {
		MPartStack stack = null;
		List<String> views = perspReader.getDefaultFastViewBarViewIds();
		if (views.size() > 0) {
			stack = layoutUtils.createStack(DEFAULT_FASTVIEW_STACK, true);
			MPartSashContainer psc = modelService.createModelElement(MPartSashContainer.class);
			psc.setHorizontal(true);
			psc.setContainerData(Integer.toString(5000));
			stack.setContainerData(Integer.toString(2500));
			psc.getChildren().add(stack);
			List<MPartSashContainer> list = modelService.findElements(perspective, null, MPartSashContainer.class);
			if (list == null || list.isEmpty()) {
				perspective.getChildren().add(psc);
			} else {
				int size = list.size();
				MPartSashContainer container = list.get(size - 1);
				container.getChildren().add(psc);
			}
			setPartState(stack, org.eclipse.ui.internal.e4.migration.InfoReader.PartState.MINIMIZED);

			for (String view : views) {
				addPlaceholderToDefaultFastViewStack(stack, view, null);
			}
		}
		return stack;
	}

	private void setPartState(MUIElement element, PartState state) {
		List<String> tags = element.getTags();
		switch (state) {
		case MINIMIZED:
			tags.add(IPresentationEngine.MINIMIZED);
			element.setVisible(false);
			break;
		case MAXIMIZED:
			tags.add(IPresentationEngine.MAXIMIZED);
			break;
		default:
			break;
		}
	}

	private void insert(MUIElement element, MUIElement refElement, InfoReader info) {
		layoutUtils.insert(element, refElement, layoutUtils.plRelToSwt(info.getRelationship()), info.getRatio());
	}

	private void insert(MUIElement element, InfoReader info) {
		insert(element, modelService.find(info.getRelative(), perspective), info);
	}

	private MPartStack createPartStack(InfoReader info) {
		String stackId = info.getId();
		if (stackId != null) {
			if (stackId.equals(StickyViewDescriptor.STICKY_FOLDER_RIGHT)) {
				stackId = "legacyStickyFolderRight"; //$NON-NLS-1$
			}
		}
		return layoutUtils.createStack(stackId, true);
	}

	private void populatePartStack(MPartStack stack, InfoReader info) {
		for (PageReader page : info.getPages()) {
			addPlaceholderToStack(stack, page.getId(), page.getLabel());
		}
		MStackElement selectedElement = (MStackElement) modelService.find(info.getActivePageId(), stack);
		if (selectedElement != null) {
			selectedElement.setToBeRendered(true);
			selectedElement.setVisible(true);
		}
		stack.setSelectedElement(selectedElement);

		// restore order of views in the stacks
		List<MStackElement> renderedViews = getRenderedViews(stack);
		if (renderedViews.size() < 2) {
			return;
		}

		int[] partOrder = info.getPartOrder();
		List<MStackElement> stackChildren = stack.getChildren();
		// unexpected situation - don't order
		if (partOrder == null || partOrder.length != renderedViews.size()) {
			return;
		}
		List<MStackElement> originalOrder = new ArrayList<>(renderedViews);
		stackChildren.clear();
		for (int element : partOrder) {
			stackChildren.add(originalOrder.get(element));
		}
		originalOrder.removeAll(stackChildren);
		stackChildren.addAll(originalOrder);
	}

	private List<MStackElement> getRenderedViews(MPartStack stack) {
		List<MStackElement> renderedViews = new ArrayList<>();
		for (MStackElement element : stack.getChildren()) {
			if (element.isToBeRendered()) {
				renderedViews.add(element);
			}
		}
		return renderedViews;
	}

	private void populatePartStack(MPartStack stack, DetachedWindowReader info) {
		for (PageReader page : info.getPages()) {
			addPlaceholderToStack(stack, page.getId(), page.getLabel());
		}
		stack.setSelectedElement((MStackElement) modelService.find(info.getActivePageId(), stack));
	}

	private void addPlaceholderToStack(MPartStack stack, String partId, String label) {
		if (partId == null || isDefaultFastView(partId)) {
			return;
		}
		MPlaceholder placeholder = createPlaceHolder(partId, label);
		if (!isToBeRendered(placeholder)) {
			placeholder.setToBeRendered(false);
		}
		addLayoutTagsToPlaceholder(placeholder, partId);
		stack.getChildren().add(placeholder);
		viewPlaceholders.put(partId, placeholder);
	}

	private void addPlaceholderToDefaultFastViewStack(MPartStack stack, String partId, String label) {
		MPlaceholder placeholder = createPlaceHolder(partId, label);
		if (!isDefaultFastView(placeholder)) {
			placeholder.setToBeRendered(false);
		}
		addLayoutTagsToPlaceholder(placeholder, partId);
		stack.getChildren().add(placeholder);
		viewPlaceholders.put(partId, placeholder);
	}

	private void addLayoutTagsToPlaceholder(MPlaceholder placeholder, String partId) {
		ViewLayoutReader viewLayout = getViewLayout(partId);
		if (viewLayout == null) {
			return;
		}
		List<String> tags = placeholder.getTags();
		if (!viewLayout.isCloseable()) {
			tags.add(IPresentationEngine.NO_CLOSE);
		}
		if (viewLayout.isStandalone()) {
			tags.add(IPresentationEngine.STANDALONE);
		}
	}

	private boolean isToBeRendered(MPlaceholder placeholder) {
		if (renderedViews == null) {
			renderedViews = perspReader.getRenderedViewIds();
		}
		return renderedViews.contains(placeholder.getElementId());
	}

	private boolean isDefaultFastView(MPlaceholder placeholder) {
		if (defaultFastViews == null) {
			defaultFastViews = perspReader.getDefaultFastViewBarViewIds();
		}
		return defaultFastViews.contains(placeholder.getElementId());
	}

	private boolean isDefaultFastView(String placeholderId) {
		if (defaultFastViews == null) {
			defaultFastViews = perspReader.getDefaultFastViewBarViewIds();
		}
		return defaultFastViews.contains(placeholderId);
	}

	private void addPerspectiveShortcutTags() {
		for (String shortcutId : perspReader.getPerspectiveShortcutIds()) {
			tags.add(ModeledPageLayout.PERSP_SHORTCUT_TAG + shortcutId);
		}
	}

	private void addActionSetTags() {
		for (String actionSetId : perspReader.getActionSetIds()) {
			tags.add(ModeledPageLayout.ACTION_SET_TAG + actionSetId);
		}
	}

	private void addShowInTags() {
		String origId = null;
		if (perspReader.isCustom()) {
			origId = perspReader.getBasicPerspectiveId();
		} else {
			origId = perspReader.getId();
		}
		ArrayList<String> list = getShowInPartFromRegistry(origId);
		if (list != null) {
			for (String showIn : list) {
				tags.add(ModeledPageLayout.SHOW_IN_PART_TAG + showIn);
			}
		}
		return;
	}

	public static ArrayList<String> getShowInPartFromRegistry(String targetId) {
		ArrayList<String> list = new ArrayList<>();
		IExtension[] extensions = getPerspectiveExtensions();
		if (extensions != null) {
			for (IExtension extension : extensions) {
				list.addAll(getExtensionShowInPartFromRegistry(extension, targetId));
			}
		}
		return list;
	}

	private static IExtension[] getPerspectiveExtensions() {
		IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(PlatformUI.PLUGIN_ID,
				IWorkbenchRegistryConstants.PL_PERSPECTIVE_EXTENSIONS);
		if (point == null) {
			return null;
		}
		IExtension[] extensions = point.getExtensions();
		return RegistryReader.orderExtensions(extensions);
	}

	private static ArrayList<String> getExtensionShowInPartFromRegistry(IExtension extension, String targetId) {
		ArrayList<String> list = new ArrayList<>();
		IConfigurationElement[] configElements = extension.getConfigurationElements();
		for (IConfigurationElement configElement : configElements) {
			String type = configElement.getName();
			if (type.equals(IWorkbenchRegistryConstants.TAG_PERSPECTIVE_EXTENSION)) {
				String id = configElement.getAttribute(IWorkbenchRegistryConstants.ATT_TARGET_ID);
				if (targetId.equals(id) || "*".equals(id)) { //$NON-NLS-1$
					list.addAll(getConfigElementShowInPartsFromRegistry(configElement));
				}
			}
		}
		return list;
	}

	private static ArrayList<String> getConfigElementShowInPartsFromRegistry(IConfigurationElement configElement) {
		ArrayList<String> list = new ArrayList<>();
		String tag = IWorkbenchRegistryConstants.TAG_SHOW_IN_PART;
		IConfigurationElement[] children = configElement.getChildren();
		for (IConfigurationElement child : children) {
			String ctype = child.getName();
			if (tag.equals(ctype)) {
				String tid = child.getAttribute(IWorkbenchRegistryConstants.ATT_ID);
				if (tid != null) {
					list.add(tid);
				}
			}
		}
		return list;
	}

	private void addNewWizardTags() {
		for (String actionId : perspReader.getNewWizardActionIds()) {
			tags.add(ModeledPageLayout.NEW_WIZARD_TAG + actionId);
		}
	}

	private void addShowViewTags() {
		for (String actionId : perspReader.getShowViewActionIds()) {
			tags.add(ModeledPageLayout.SHOW_VIEW_TAG + actionId);
		}
	}

	private void addHiddenItems() {
		String comma = ","; //$NON-NLS-1$
		StringBuilder persistedValue = new StringBuilder();
		for (String elementId : perspReader.getHiddenMenuItemIds()) {
			persistedValue.append(ModeledPageLayout.HIDDEN_MENU_PREFIX);
			persistedValue.append(elementId).append(comma);
		}
		for (String elementId : perspReader.getHiddenToolbarItemIds()) {
			persistedValue.append(ModeledPageLayout.HIDDEN_TOOLBAR_PREFIX);
			persistedValue.append(elementId).append(comma);
		}
		perspective.getPersistedState().put(ModeledPageLayout.HIDDEN_ITEMS_KEY, persistedValue.toString());
	}

	private ViewLayoutReader getViewLayout(String viewId) {
		if (viewLayouts == null) {
			viewLayouts = perspReader.getViewLayouts();
		}
		return viewLayouts.get(viewId);
	}

	Collection<MPlaceholder> getPlaceholders() {
		return viewPlaceholders.values();
	}

	MPlaceholder getEditorAreaPlaceholder() {
		return editorAreaPlaceholder;
	}

	MPlaceholder createPlaceHolder(String str, String label) {
		MPlaceholder placeholder = null;
		placeholder = modelService.createModelElement(MPlaceholder.class);
		placeholder.setElementId(str);
		if (modelService.getPartDescriptor(str) == null) {
			placeholder.getTransientData().put(IWorkbenchConstants.TAG_LABEL, label);
		}
		return placeholder;
	}

}
