001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2009-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel.task;
028
029import static org.opends.messages.AdminToolMessages.*;
030import static org.opends.server.util.CollectionUtils.*;
031
032import java.util.ArrayList;
033import java.util.Collection;
034import java.util.Collections;
035import java.util.HashSet;
036import java.util.LinkedHashSet;
037import java.util.List;
038import java.util.Map;
039import java.util.Set;
040
041import javax.swing.SwingUtilities;
042
043import org.forgerock.i18n.LocalizableMessage;
044import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
045import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
046import org.opends.guitools.controlpanel.ui.ProgressDialog;
047import org.opends.guitools.controlpanel.util.Utilities;
048import org.opends.server.types.AttributeType;
049import org.opends.server.types.ObjectClass;
050import org.opends.server.types.OpenDsException;
051import org.opends.server.types.Schema;
052
053/**
054 * The task that is in charge of modifying an attribute definition (and all
055 * the references to this attribute).
056 */
057public class ModifyAttributeTask extends Task
058{
059  private AttributeType oldAttribute;
060  private AttributeType newAttribute;
061
062  /**
063   * The constructor of the task.
064   * @param info the control panel info.
065   * @param dlg the progress dialog that shows the progress of the task.
066   * @param oldAttribute the old attribute definition.
067   * @param newAttribute the new attribute definition.
068   */
069  public ModifyAttributeTask(ControlPanelInfo info, ProgressDialog dlg,
070      AttributeType oldAttribute, AttributeType newAttribute)
071  {
072    super(info, dlg);
073    if (oldAttribute == null)
074    {
075      throw new IllegalArgumentException("oldAttribute cannot be null.");
076    }
077    if (newAttribute == null)
078    {
079      throw new IllegalArgumentException("newAttribute cannot be null.");
080    }
081    this.oldAttribute = oldAttribute;
082    this.newAttribute = newAttribute;
083  }
084
085  /** {@inheritDoc} */
086  public Type getType()
087  {
088    return Type.MODIFY_SCHEMA_ELEMENT;
089  }
090
091  /** {@inheritDoc} */
092  public LocalizableMessage getTaskDescription()
093  {
094    return INFO_CTRL_PANEL_MODIFY_ATTRIBUTE_TASK_DESCRIPTION.get(
095        oldAttribute.getNameOrOID());
096  }
097
098  /** {@inheritDoc} */
099  public boolean canLaunch(Task taskToBeLaunched,
100      Collection<LocalizableMessage> incompatibilityReasons)
101  {
102    boolean canLaunch = true;
103    if (state == State.RUNNING &&
104        (taskToBeLaunched.getType() == Task.Type.DELETE_SCHEMA_ELEMENT ||
105         taskToBeLaunched.getType() == Task.Type.MODIFY_SCHEMA_ELEMENT ||
106         taskToBeLaunched.getType() == Task.Type.NEW_SCHEMA_ELEMENT))
107    {
108      incompatibilityReasons.add(getIncompatibilityMessage(this,
109            taskToBeLaunched));
110      canLaunch = false;
111    }
112    return canLaunch;
113  }
114
115  /** {@inheritDoc} */
116  public Set<String> getBackends()
117  {
118    return Collections.emptySet();
119  }
120
121  /** {@inheritDoc} */
122  protected List<String> getCommandLineArguments()
123  {
124    return Collections.emptyList();
125  }
126
127  /** {@inheritDoc} */
128  protected String getCommandLinePath()
129  {
130    return null;
131  }
132
133  /** {@inheritDoc} */
134  public void runTask()
135  {
136    try
137    {
138      updateSchema();
139      state = State.FINISHED_SUCCESSFULLY;
140    }
141    catch (Throwable t)
142    {
143      // TODO
144      //revertChanges();
145      lastException = t;
146      state = State.FINISHED_WITH_ERROR;
147    }
148  }
149
150  private AttributeType getAttributeToAdd(AttributeType attrToDelete)
151  {
152    if (attrToDelete.equals(oldAttribute))
153    {
154      return newAttribute;
155    }
156    else if (oldAttribute.equals(attrToDelete.getSuperiorType()))
157    {
158      ArrayList<String> allNames = new ArrayList<>(attrToDelete.getNormalizedNames());
159      Map<String, List<String>> extraProperties =
160        DeleteSchemaElementsTask.cloneExtraProperties(attrToDelete);
161      AttributeType newSuperior = newAttribute;
162      return new AttributeType(
163          "",
164          attrToDelete.getPrimaryName(),
165          allNames,
166          attrToDelete.getOID(),
167          attrToDelete.getDescription(),
168          newSuperior,
169          attrToDelete.getSyntax(),
170          attrToDelete.getApproximateMatchingRule(),
171          attrToDelete.getEqualityMatchingRule(),
172          attrToDelete.getOrderingMatchingRule(),
173          attrToDelete.getSubstringMatchingRule(),
174          attrToDelete.getUsage(),
175          attrToDelete.isCollective(),
176          attrToDelete.isNoUserModification(),
177          attrToDelete.isObsolete(),
178          attrToDelete.isSingleValue(),
179          extraProperties);
180    }
181    else
182    {
183      // Nothing to be changed in the definition of the attribute itself.
184      return attrToDelete;
185    }
186  }
187
188  private ObjectClass getObjectClassToAdd(ObjectClass ocToDelete)
189  {
190    boolean containsAttribute =
191      ocToDelete.getRequiredAttributeChain().contains(oldAttribute) ||
192      ocToDelete.getOptionalAttributeChain().contains(oldAttribute);
193    if (containsAttribute)
194    {
195      ArrayList<String> allNames = new ArrayList<>(ocToDelete.getNormalizedNames());
196      Map<String, List<String>> extraProperties =
197        DeleteSchemaElementsTask.cloneExtraProperties(ocToDelete);
198      Set<AttributeType> required = new HashSet<>(ocToDelete.getRequiredAttributes());
199      Set<AttributeType> optional = new HashSet<>(ocToDelete.getOptionalAttributes());
200      if (required.contains(oldAttribute))
201      {
202        required.remove(oldAttribute);
203        required.add(newAttribute);
204      }
205      else if (optional.contains(oldAttribute))
206      {
207        optional.remove(oldAttribute);
208        optional.add(newAttribute);
209      }
210      return new ObjectClass("",
211          ocToDelete.getPrimaryName(),
212          allNames,
213          ocToDelete.getOID(),
214          ocToDelete.getDescription(),
215          ocToDelete.getSuperiorClasses(),
216          required,
217          optional,
218          ocToDelete.getObjectClassType(),
219          ocToDelete.isObsolete(),
220          extraProperties);
221    }
222    else
223    {
224      // Nothing to be changed in the definition of the object class itself.
225      return ocToDelete;
226    }
227  }
228
229  /**
230   * Updates the schema.
231   * @throws OpenDsException if an error occurs.
232   */
233  private void updateSchema() throws OpenDsException
234  {
235    Schema schema = getInfo().getServerDescriptor().getSchema();
236    ArrayList<AttributeType> attrs = newArrayList(oldAttribute);
237    LinkedHashSet<AttributeType> attrsToDelete =
238      DeleteSchemaElementsTask.getOrderedAttributesToDelete(attrs, schema);
239    LinkedHashSet<ObjectClass> ocsToDelete =
240      DeleteSchemaElementsTask.getOrderedObjectClassesToDeleteFromAttrs(
241          attrsToDelete, schema);
242
243    LinkedHashSet<AttributeType> attrsToAdd = new LinkedHashSet<>();
244    ArrayList<AttributeType> lAttrsToDelete = new ArrayList<>(attrsToDelete);
245    for (int i = lAttrsToDelete.size() - 1; i >= 0; i--)
246    {
247      AttributeType attrToAdd = getAttributeToAdd(lAttrsToDelete.get(i));
248      if (attrToAdd != null)
249      {
250        attrsToAdd.add(attrToAdd);
251      }
252    }
253
254    ArrayList<ObjectClass> lOcsToDelete = new ArrayList<>(ocsToDelete);
255    LinkedHashSet<ObjectClass> ocsToAdd = new LinkedHashSet<>();
256    for (int i = lOcsToDelete.size() - 1; i >= 0; i--)
257    {
258      ocsToAdd.add(getObjectClassToAdd(lOcsToDelete.get(i)));
259    }
260
261    SwingUtilities.invokeLater(new Runnable()
262    {
263      public void run()
264      {
265        getProgressDialog().appendProgressHtml(Utilities.applyFont(
266            INFO_CTRL_PANEL_EXPLANATION_TO_MODIFY_ATTRIBUTE.get(
267                oldAttribute.getNameOrOID())+"<br><br>",
268                ColorAndFontConstants.progressFont));
269      }
270    });
271
272    DeleteSchemaElementsTask deleteTask =
273      new DeleteSchemaElementsTask(getInfo(), getProgressDialog(), ocsToDelete,
274          attrsToDelete);
275    deleteTask.runTask();
276
277    SwingUtilities.invokeLater(new Runnable()
278    {
279      public void run()
280      {
281        getProgressDialog().appendProgressHtml(Utilities.applyFont("<br><br>",
282                ColorAndFontConstants.progressFont));
283      }
284    });
285
286    NewSchemaElementsTask createTask =
287      new NewSchemaElementsTask(getInfo(), getProgressDialog(), ocsToAdd,
288          attrsToAdd);
289    createTask.runTask();
290
291    notifyConfigurationElementCreated(newAttribute);
292  }
293}