| /******************************************************************************** |
| * Copyright (c) 2008 - 2017 Profactor GmbH, TU Wien ACIN, fortiss GmbH |
| * 2019 Johannes Kepler University Linz |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Gerhard Ebenhofer, Alois Zoitl, Matthias Plasch, Monika Wenger, Gerd Kainz, Martin Jobst |
| * - initial API and implementation and/or initial documentation |
| * Bianca Wiesmayr |
| * - improve name proposals |
| ********************************************************************************/ |
| package org.eclipse.fordiac.ide.model; |
| |
| import static org.eclipse.fordiac.ide.model.FordiacKeywords.RESERVED_KEYWORDS; |
| |
| import java.text.MessageFormat; |
| import java.util.Set; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import java.util.stream.Collectors; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.emf.common.util.BasicEList; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.fordiac.ide.model.Palette.Palette; |
| import org.eclipse.fordiac.ide.model.data.DataType; |
| import org.eclipse.fordiac.ide.model.data.StructuredType; |
| import org.eclipse.fordiac.ide.model.libraryElement.AdapterType; |
| import org.eclipse.fordiac.ide.model.libraryElement.Algorithm; |
| import org.eclipse.fordiac.ide.model.libraryElement.Application; |
| import org.eclipse.fordiac.ide.model.libraryElement.BaseFBType; |
| import org.eclipse.fordiac.ide.model.libraryElement.BasicFBType; |
| import org.eclipse.fordiac.ide.model.libraryElement.Device; |
| import org.eclipse.fordiac.ide.model.libraryElement.ECC; |
| import org.eclipse.fordiac.ide.model.libraryElement.ECState; |
| import org.eclipse.fordiac.ide.model.libraryElement.FBNetworkElement; |
| import org.eclipse.fordiac.ide.model.libraryElement.FBType; |
| import org.eclipse.fordiac.ide.model.libraryElement.IInterfaceElement; |
| import org.eclipse.fordiac.ide.model.libraryElement.INamedElement; |
| import org.eclipse.fordiac.ide.model.libraryElement.InterfaceList; |
| import org.eclipse.fordiac.ide.model.libraryElement.LibraryElement; |
| import org.eclipse.fordiac.ide.model.libraryElement.Resource; |
| import org.eclipse.fordiac.ide.model.libraryElement.Segment; |
| import org.eclipse.fordiac.ide.model.libraryElement.SubAppType; |
| import org.eclipse.fordiac.ide.model.libraryElement.SystemConfiguration; |
| import org.eclipse.fordiac.ide.model.typelibrary.DataTypeLibrary; |
| import org.eclipse.fordiac.ide.model.typelibrary.TypeLibrary; |
| import org.eclipse.fordiac.ide.ui.errormessages.ErrorMessenger; |
| |
| public final class NameRepository { |
| |
| private static final Pattern END_IN_NUMBER_PATTERN = Pattern.compile("^.*\\d$"); //$NON-NLS-1$ |
| private static final Pattern GET_LAST_NUMBER_PATTERN = Pattern.compile("\\d+$"); //$NON-NLS-1$ |
| |
| private NameRepository() { |
| // empty private constructor |
| } |
| |
| public static String createUniqueTypeName(final LibraryElement type) { |
| final TypeLibrary typeLibrary = type.getTypeLibrary(); |
| final Palette blockTypeLib = typeLibrary.getBlockTypeLib(); |
| String typeName = type.getName(); |
| |
| if(type instanceof DataType) { |
| final DataTypeLibrary dataTypeLibrary = typeLibrary.getDataTypeLibrary(); |
| while (dataTypeLibrary.getTypeIfExists(typeName) != null) { |
| typeName = createUniqueName(type.getName(), typeName); |
| } |
| return typeName; |
| } |
| |
| if (type instanceof FBType) { |
| while (blockTypeLib.getFBTypeEntry(typeName) != null) { |
| typeName = createUniqueName(type.getName(), typeName); |
| } |
| return typeName; |
| } |
| |
| if (type instanceof SubAppType) { |
| while (blockTypeLib.getSubAppTypeEntry(typeName) != null) { |
| typeName = createUniqueName(type.getName(), typeName); |
| } |
| return typeName; |
| } |
| |
| if (type instanceof AdapterType) { |
| while (blockTypeLib.getAdapterTypeEntry(typeName) != null) { |
| typeName = createUniqueName(type.getName(), typeName); |
| } |
| return typeName; |
| } |
| |
| return null; |
| |
| } |
| |
| |
| /** |
| * Check and if necessary adapt the given name proposal so that it is a valid |
| * name for the given INamedElement |
| * |
| * This method expects that the element is already correctly inserted in the |
| * model and that its eContainer returns the correct container. The nameProposal |
| * needs to be a valid identifier |
| * |
| * @param element the element for which the name should be created |
| * @param nameProposal a proposal for a name of the element |
| * @return a valid unique element name |
| */ |
| public static String createUniqueName(final INamedElement element, final String nameProposal) { |
| Assert.isTrue(IdentifierVerifyer.isValidIdentifier(nameProposal), |
| "The given name proposal is not a valid identifier!"); //$NON-NLS-1$ |
| Assert.isNotNull(element.eContainer(), |
| "For a correct operation createuniqueName expects that the model element is already added in its containing model!"); //$NON-NLS-1$ |
| |
| String retVal = nameProposal; |
| if (element instanceof IInterfaceElement) { |
| // for interface elements we need to check if it not a reserved keyword |
| retVal = checkReservedKeyWords(nameProposal); |
| } |
| return getUniqueName(getRefNames(element), retVal); |
| } |
| |
| /** |
| * Check if the given nameProposal is a valid name for the given named element. |
| * |
| * @param element the named element for which a new name proposal should be |
| * checked |
| * @param nameProposal the new name to be checked |
| * @return true if the nameProposal is a valid new name for the named element |
| */ |
| public static boolean isValidName(final INamedElement element, final String nameProposal) { |
| Assert.isNotNull(element.eContainer(), |
| "For a correct operation createuniqueName expects that the model element is already added in its containing model!"); //$NON-NLS-1$ |
| |
| if (!IdentifierVerifyer.isValidIdentifier(nameProposal)) { |
| ErrorMessenger.popUpErrorMessage( |
| MessageFormat.format(Messages.NameRepository_NameNotAValidIdentifier, nameProposal)); |
| return false; |
| } |
| if (element instanceof IInterfaceElement && RESERVED_KEYWORDS.contains(nameProposal)) { |
| ErrorMessenger.popUpErrorMessage( |
| MessageFormat.format(Messages.NameRepository_NameReservedKeyWord, nameProposal)); |
| return false; |
| } |
| |
| if (getRefNames(element).contains(nameProposal)) { |
| ErrorMessenger.popUpErrorMessage( |
| MessageFormat.format(Messages.NameRepository_NameAlreadyExists, nameProposal)); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| private static Set<String> getRefNames(final INamedElement refElement) { |
| EList<? extends INamedElement> elementsList = null; |
| |
| // TODO consider moving this instance of cascade into the model utilizing the |
| // inheritance hierarchy to our advantage |
| if (refElement instanceof Algorithm) { |
| elementsList = ((BasicFBType) ((Algorithm) refElement).eContainer()).getAlgorithm(); |
| } else if (refElement instanceof Application) { |
| elementsList = ((Application) refElement).getAutomationSystem().getApplication(); |
| } else if (refElement instanceof Device) { |
| elementsList = ((Device) refElement).getSystemConfiguration().getDevices(); |
| } else if (refElement instanceof FBNetworkElement) { |
| elementsList = ((FBNetworkElement) refElement).getFbNetwork().getNetworkElements(); |
| } else if (refElement instanceof Resource) { |
| elementsList = ((Resource) refElement).getDevice().getResource(); |
| } else if (refElement instanceof Segment) { |
| elementsList = ((SystemConfiguration) ((Segment) refElement).eContainer()).getSegments(); |
| } else if (refElement instanceof ECState) { |
| elementsList = ((ECC) ((ECState) refElement).eContainer()).getECState(); |
| } else if (refElement instanceof IInterfaceElement) { |
| if (((IInterfaceElement) refElement).eContainer() instanceof StructuredType) { |
| elementsList = ((StructuredType) ((IInterfaceElement) refElement).eContainer()).getMemberVariables(); |
| } else { |
| final EList<INamedElement> elements = new BasicEList<>(); |
| InterfaceList interfaceList = null; |
| if (((IInterfaceElement) refElement).eContainer() instanceof InterfaceList) { |
| interfaceList = (InterfaceList) ((IInterfaceElement) refElement).eContainer(); |
| } else { |
| // this is an internal variable |
| interfaceList = ((BaseFBType) ((IInterfaceElement) refElement).eContainer()).getInterfaceList(); |
| } |
| elements.addAll(interfaceList.getAllInterfaceElements()); |
| if (interfaceList.eContainer() instanceof BaseFBType) { // has internal variables |
| elements.addAll(((BaseFBType) interfaceList.eContainer()).getInternalVars()); |
| } |
| elementsList = elements; |
| } |
| } else { |
| throw new IllegalArgumentException( |
| "Refenrence list for given class not available: " + refElement.getClass().toString()); //$NON-NLS-1$ |
| } |
| |
| return elementsList.stream().filter(element -> element != refElement).map(INamedElement::getName) |
| .collect(Collectors.toSet()); |
| } |
| |
| /** |
| * Generating a unique name for a name proposal which is definitely not in the |
| * list of given existing names |
| * |
| * If the proposed name is already found in the list an '_' and a consecutive |
| * number is appended to the proposed name. The number incremented until a |
| * unique name is found. |
| * |
| * @param existingNameList the list of names already existing in the context |
| * @param nameProposal a proposal for a name as starting point |
| * @return a unique name |
| */ |
| private static String getUniqueName(final Set<String> existingNameList, final String nameProposal) { |
| String temp = nameProposal; |
| while (existingNameList.contains(temp)) { |
| temp = createUniqueName(nameProposal, temp); |
| } |
| return temp; |
| } |
| |
| private static String createUniqueName(final String nameProposal, String temp) { |
| if (END_IN_NUMBER_PATTERN.matcher(temp).matches()) { |
| final Matcher matchNumber = GET_LAST_NUMBER_PATTERN.matcher(temp); |
| matchNumber.find(); |
| final int number = Integer.parseInt(temp.substring(matchNumber.start(), matchNumber.end())) + 1; |
| temp = temp.substring(0, matchNumber.start()) + number; // $NON-NLS-1$ |
| } else { |
| temp = nameProposal + "_" + 1; //$NON-NLS-1$ |
| } |
| return temp; |
| } |
| |
| private static String checkReservedKeyWords(final String name) { |
| if (RESERVED_KEYWORDS.contains(name.toUpperCase())) { |
| return name + "1"; //$NON-NLS-1$ |
| } |
| return name; |
| } |
| |
| } |