| /******************************************************************************* |
| * Copyright (c) 2016 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 |
| * Jens Lukowski/Innoopract - initial renaming/restructuring |
| * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.DOMModelImpl |
| * modified in order to process JSON Objects. |
| * Alina Marin <alina@mx1.ibm.com> - fixed some stuff to improve the synch between the editor and the model. |
| *******************************************************************************/ |
| package org.eclipse.wst.json.core.internal.document; |
| |
| import org.eclipse.wst.json.core.document.IJSONArray; |
| import org.eclipse.wst.json.core.document.IJSONDocument; |
| import org.eclipse.wst.json.core.document.IJSONModel; |
| import org.eclipse.wst.json.core.document.IJSONNode; |
| import org.eclipse.wst.json.core.document.IJSONObject; |
| import org.eclipse.wst.json.core.document.IJSONPair; |
| import org.eclipse.wst.json.core.document.IJSONValue; |
| import org.eclipse.wst.json.core.internal.Logger; |
| import org.eclipse.wst.json.core.regions.JSONRegionContexts; |
| import org.eclipse.wst.sse.core.internal.model.AbstractStructuredModel; |
| import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.events.IStructuredDocumentListener; |
| import org.eclipse.wst.sse.core.internal.provisional.events.NewDocumentEvent; |
| import org.eclipse.wst.sse.core.internal.provisional.events.NoChangeEvent; |
| import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent; |
| import org.eclipse.wst.sse.core.internal.provisional.events.RegionsReplacedEvent; |
| import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentRegionsReplacedEvent; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; |
| import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; |
| import org.w3c.dom.Document; |
| |
| /** |
| * SSE {@link IStructuredDocument} implementation for JSON. |
| */ |
| public class JSONModelImpl extends AbstractStructuredModel implements |
| IStructuredDocumentListener, IJSONModel { |
| |
| private static String TRACE_PARSER_MANAGEMENT_EXCEPTION = "parserManagement"; //$NON-NLS-1$ |
| private Object active = null; |
| private JSONDocumentImpl document = null; |
| private ISourceGenerator generator = null; |
| private JSONModelNotifier notifier = null; |
| private JSONModelParser parser = null; |
| private boolean refresh = false; |
| private JSONModelUpdater updater = null; |
| |
| /** |
| * JSONModelImpl constructor |
| */ |
| public JSONModelImpl() { |
| super(); |
| this.document = (JSONDocumentImpl) internalCreateDocument(); |
| } |
| |
| /** |
| * This API allows clients to declare that they are about to make a "large" |
| * change to the model. This change might be in terms of content or it might |
| * be in terms of the model id or base location. |
| * |
| * Note that in the case of embedded calls, notification to listeners is |
| * sent only once. |
| * |
| * Note that the client who is making these changes has the responsibility |
| * to restore the models state once finished with the changes. See |
| * getMemento and restoreState. |
| * |
| * The method isModelStateChanging can be used by a client to determine if |
| * the model is already in a change sequence. |
| */ |
| public void aboutToChangeModel() { |
| super.aboutToChangeModel(); |
| // technically, no need to call beginChanging so often, |
| // since aboutToChangeModel can be nested. |
| // but will leave as is for this release. |
| // see modelChanged, and be sure stays coordinated there. |
| getModelNotifier().beginChanging(); |
| } |
| |
| public void aboutToReinitializeModel() { |
| JSONModelNotifier notifier = getModelNotifier(); |
| notifier.cancelPending(); |
| super.aboutToReinitializeModel(); |
| } |
| |
| protected void pairReplaced(IJSONObject element, IJSONPair newAttr, |
| IJSONPair oldAttr) { |
| if (element == null) |
| return; |
| if (getActiveParser() == null) { |
| JSONModelUpdater updater = getModelUpdater(); |
| setActive(updater); |
| updater.initialize(); |
| updater.replaceAttr(element, newAttr, oldAttr); |
| setActive(null); |
| } |
| getModelNotifier().pairReplaced(element, newAttr, oldAttr); |
| } |
| |
| /** |
| * This API allows a client controlled way of notifying all ModelEvent |
| * listners that the model has been changed. This method is a matched pair |
| * to aboutToChangeModel, and must be called after aboutToChangeModel ... or |
| * some listeners could be left waiting indefinitely for the changed event. |
| * So, its suggested that changedModel always be in a finally clause. |
| * Likewise, a client should never call changedModel without calling |
| * aboutToChangeModel first. |
| * |
| * In the case of embedded calls, the notification is just sent once. |
| * |
| */ |
| public void changedModel() { |
| // NOTE: the order of 'changedModel' and 'endChanging' is significant. |
| // By calling changedModel first, this basically decrements the |
| // "isChanging" counter |
| // in super class and when zero all listeners to model state events |
| // will be notified |
| // that the model has been changed. 'endChanging' will notify all |
| // deferred adapters. |
| // So, the significance of order is that adapters (and methods they |
| // call) |
| // can count on the state of model "isChanging" to be accurate. |
| // But, remember, that this means the "modelChanged" event can be |
| // received before all |
| // adapters have finished their processing. |
| // NOTE NOTE: The above note is obsolete in fact (though still states |
| // issue correctly). |
| // Due to popular demand, the order of these calls were reversed and |
| // behavior |
| // changed on 07/22/2004. |
| // |
| // see also |
| // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=4302 |
| // for motivation for this 'on verge of' call. |
| // this could be improved in future if notifier also used counting |
| // flag to avoid nested calls. If/when changed be sure to check if |
| // aboutToChangeModel needs any changes too. |
| if (isModelChangeStateOnVergeOfEnding()) { |
| // end lock before noticiation loop, since directly or indirectly |
| // we may be "called from foriegn code" during notification. |
| endLock(); |
| // we null out here to avoid spurious"warning" message while debug |
| // tracing is enabled |
| fLockObject = null; |
| // the notifier is what controls adaper notification, which |
| // should be sent out before the 'modelChanged' event. |
| getModelNotifier().endChanging(); |
| } |
| // changedModel handles 'nesting', so only one event sent out |
| // when mulitple calls to 'aboutToChange/Changed'. |
| super.changedModel(); |
| handleRefresh(); |
| } |
| |
| /** |
| * childReplaced method |
| * |
| * @param parentNode |
| * org.w3c.dom.Node |
| * @param newChild |
| * org.w3c.dom.Node |
| * @param oldChild |
| * org.w3c.dom.Node |
| */ |
| protected void childReplaced(IJSONNode parentNode, IJSONNode newChild, |
| IJSONNode oldChild) { |
| if (parentNode == null) |
| return; |
| if (getActiveParser() == null) { |
| JSONModelUpdater updater = getModelUpdater(); |
| setActive(updater); |
| updater.initialize(); |
| updater.replaceChild(parentNode, newChild, oldChild); |
| setActive(null); |
| } |
| getModelNotifier().childReplaced(parentNode, newChild, oldChild); |
| } |
| |
| /** |
| */ |
| // protected void documentTypeChanged() { |
| // if (this.refresh) |
| // return; |
| // // unlike 'resfresh', 'reinitialize' finishes loop |
| // // and flushes remaining notification que before |
| // // actually reinitializing. |
| // // ISSUE: should reinit be used instead of handlerefresh? |
| // // this.setReinitializeNeeded(true); |
| // if (this.active != null || getModelNotifier().isChanging()) |
| // return; // defer |
| // handleRefresh(); |
| // } |
| |
| // protected void editableChanged(Node node) { |
| // if (node != null) { |
| // getModelNotifier().editableChanged(node); |
| // } |
| // } |
| |
| /** |
| */ |
| // protected void endTagChanged(IJSONObject element) { |
| // if (element == null) |
| // return; |
| // if (getActiveParser() == null) { |
| // JSONModelUpdater updater = getModelUpdater(); |
| // setActive(updater); |
| // updater.initialize(); |
| // // updater.changeEndTag(element); |
| // setActive(null); |
| // } |
| // getModelNotifier().endTagChanged(element); |
| // } |
| |
| /** |
| */ |
| private JSONModelParser getActiveParser() { |
| if (this.parser == null) |
| return null; |
| if (this.parser != this.active) |
| return null; |
| return this.parser; |
| } |
| |
| /** |
| */ |
| private JSONModelUpdater getActiveUpdater() { |
| if (this.updater == null) |
| return null; |
| if (this.updater != this.active) |
| return null; |
| return this.updater; |
| } |
| |
| @Override |
| public Object getAdapter(Class adapter) { |
| if (Document.class.equals(adapter)) |
| return getDocument(); |
| return super.getAdapter(adapter); |
| } |
| |
| @Override |
| public IJSONDocument getDocument() { |
| return this.document; |
| } |
| |
| public ISourceGenerator getGenerator() { |
| if (this.generator == null) { |
| this.generator = JSONGeneratorImpl.getInstance(); |
| } |
| return this.generator; |
| } |
| |
| @Override |
| public IndexedRegion getIndexedRegion(int offset) { |
| if (this.document == null) |
| return null; |
| // search in document children |
| IJSONNode parent = null; |
| int length = this.document.getEndOffset(); |
| if (offset * 2 < length) { |
| // search from the first |
| IJSONNode child = (IJSONNode) this.document.getFirstChild(); |
| while (child != null) { |
| if (child.getEndOffset() <= offset) { |
| child = (IJSONNode) child.getNextSibling(); |
| continue; |
| } |
| if (child.getStartOffset() > offset) { |
| break; |
| } |
| IStructuredDocumentRegion startStructuredDocumentRegion = child |
| .getStartStructuredDocumentRegion(); |
| if (startStructuredDocumentRegion != null) { |
| if (startStructuredDocumentRegion.getEnd() > offset) |
| return child; |
| } |
| IStructuredDocumentRegion endStructuredDocumentRegion = child |
| .getEndStructuredDocumentRegion(); |
| if (endStructuredDocumentRegion != null) { |
| if (endStructuredDocumentRegion.getStart() <= offset) { |
| if (child instanceof IJSONPair) { |
| IJSONValue value = ((IJSONPair)child).getValue(); |
| if (value instanceof IJSONObject || value instanceof IJSONArray) { |
| if (value.getStartOffset() < offset) { |
| child = value; |
| continue; |
| } |
| } |
| } |
| return child; |
| } |
| } |
| // dig more |
| parent = child; |
| if (parent != null |
| && parent.getNodeType() == IJSONNode.PAIR_NODE) { |
| IJSONPair pair = (IJSONPair) parent; |
| child = pair.getValue(); |
| } else { |
| child = (IJSONNode) parent.getFirstChild(); |
| } |
| } |
| } else { |
| // search from the last |
| IJSONNode child = (IJSONNode) this.document.getLastChild(); |
| while (child != null) { |
| if (child.getStartOffset() > offset) { |
| child = (IJSONNode) child.getPreviousSibling(); |
| continue; |
| } |
| if (child.getEndOffset() <= offset) { |
| break; |
| } |
| IStructuredDocumentRegion startStructuredDocumentRegion = child |
| .getStartStructuredDocumentRegion(); |
| if (startStructuredDocumentRegion != null) { |
| if (startStructuredDocumentRegion.getEnd() > offset) |
| return child; |
| } |
| IStructuredDocumentRegion endStructuredDocumentRegion = child |
| .getEndStructuredDocumentRegion(); |
| if (endStructuredDocumentRegion != null) { |
| if (endStructuredDocumentRegion.getStart() <= offset) { |
| if (child instanceof IJSONPair) { |
| IJSONValue value = ((IJSONPair)child).getValue(); |
| if (value instanceof IJSONObject || value instanceof IJSONArray) { |
| if (value.getStartOffset() < offset) { |
| child = value; |
| continue; |
| } |
| } |
| } |
| return child; |
| } |
| } |
| // dig more |
| parent = child; |
| if (parent != null |
| && parent.getNodeType() == IJSONNode.PAIR_NODE) { |
| IJSONPair pair = (IJSONPair) parent; |
| child = pair.getValue(); |
| } else { |
| child = (IJSONNode) parent.getLastChild(); |
| } |
| } |
| } |
| return parent != null ? parent : document.getFirstChild(); |
| } |
| |
| /** |
| */ |
| public JSONModelNotifier getModelNotifier() { |
| if (this.notifier == null) { |
| this.notifier = new JSONModelNotifierImpl(); |
| } |
| return this.notifier; |
| } |
| |
| /** |
| */ |
| private JSONModelParser getModelParser() { |
| if (this.parser == null) { |
| this.parser = createModelParser(); |
| } |
| return this.parser; |
| } |
| |
| protected JSONModelParser createModelParser() { |
| return new JSONModelParser(this); |
| } |
| |
| /** |
| */ |
| private JSONModelUpdater getModelUpdater() { |
| if (this.updater == null) { |
| this.updater = createModelUpdater(); |
| } |
| return this.updater; |
| } |
| |
| protected JSONModelUpdater createModelUpdater() { |
| return new JSONModelUpdater(this); |
| } |
| |
| /** |
| */ |
| private void handleRefresh() { |
| if (!this.refresh) |
| return; |
| JSONModelNotifier notifier = getModelNotifier(); |
| boolean isChanging = notifier.isChanging(); |
| if (!isChanging) |
| notifier.beginChanging(true); |
| JSONModelParser parser = getModelParser(); |
| setActive(parser); |
| this.document.removeChildNodes(); |
| try { |
| this.refresh = false; |
| parser.replaceStructuredDocumentRegions(getStructuredDocument() |
| .getRegionList(), null); |
| } catch (Exception ex) { |
| Logger.logException(ex); |
| } finally { |
| setActive(null); |
| if (!isChanging) |
| notifier.endChanging(); |
| } |
| } |
| |
| protected IJSONDocument internalCreateDocument() { |
| JSONDocumentImpl document = new JSONDocumentImpl(); |
| document.setModel(this); |
| return document; |
| } |
| |
| boolean isReparsing() { |
| return (active != null); |
| } |
| |
| @Override |
| public void newModel(NewDocumentEvent structuredDocumentEvent) { |
| if (structuredDocumentEvent == null) |
| return; |
| IStructuredDocument structuredDocument = structuredDocumentEvent |
| .getStructuredDocument(); |
| if (structuredDocument == null) |
| return; |
| // this should not happen, but for the case |
| if (fStructuredDocument != null |
| && fStructuredDocument != structuredDocument) |
| setStructuredDocument(structuredDocument); |
| |
| internalSetNewDocument(structuredDocument); |
| } |
| |
| private void internalSetNewDocument(IStructuredDocument structuredDocument) { |
| if (structuredDocument == null) |
| return; |
| IStructuredDocumentRegionList flatNodes = structuredDocument |
| .getRegionList(); |
| if ((flatNodes == null) || (flatNodes.getLength() == 0)) { |
| return; |
| } |
| if (this.document == null) |
| return; // being constructed |
| |
| JSONModelUpdater updater = getActiveUpdater(); |
| if (updater != null) { // being updated |
| try { |
| updater.replaceStructuredDocumentRegions(flatNodes, null); |
| } catch (Exception ex) { |
| Logger.logException(ex); |
| this.refresh = true; |
| handleRefresh(); |
| } finally { |
| setActive(null); |
| } |
| // // for new model, we might need to |
| // // re-init, e.g. if someone calls setText |
| // // on an existing model |
| // checkForReinit(); |
| return; |
| } |
| JSONModelNotifier notifier = getModelNotifier(); |
| boolean isChanging = notifier.isChanging(); |
| // call even if changing to notify doing new model |
| getModelNotifier().beginChanging(true); |
| JSONModelParser parser = getModelParser(); |
| setActive(parser); |
| this.document.removeChildNodes(); |
| try { |
| parser.replaceStructuredDocumentRegions(flatNodes, null); |
| } catch (Exception ex) { |
| Logger.logException(ex); |
| // meaningless to refresh, because the result might be the same |
| } finally { |
| setActive(null); |
| if (!isChanging) { |
| getModelNotifier().endChanging(); |
| } |
| // ignore refresh |
| this.refresh = false; |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.wst.sse.core.internal.provisional.events. |
| * IStructuredDocumentListener |
| * #noChange(org.eclipse.wst.sse.core.internal.provisional |
| * .events.NoChangeEvent) |
| */ |
| @Override |
| public void noChange(NoChangeEvent event) { |
| JSONModelUpdater updater = getActiveUpdater(); |
| if (updater != null) { // being updated |
| // cleanup updater staffs |
| try { |
| updater.replaceStructuredDocumentRegions(null, null); |
| } catch (Exception ex) { |
| Logger.logException(ex); |
| this.refresh = true; |
| handleRefresh(); |
| } finally { |
| setActive(null); |
| } |
| // I guess no chanage means the model could not need re-init |
| // checkForReinit(); |
| return; |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.wst.sse.core.internal.provisional.events. |
| * IStructuredDocumentListener |
| * #nodesReplaced(org.eclipse.wst.sse.core.internal |
| * .provisional.events.StructuredDocumentRegionsReplacedEvent) |
| */ |
| @Override |
| public void nodesReplaced(StructuredDocumentRegionsReplacedEvent event) { |
| if (event == null) |
| return; |
| IStructuredDocumentRegionList oldStructuredDocumentRegions = event |
| .getOldStructuredDocumentRegions(); |
| IStructuredDocumentRegionList newStructuredDocumentRegions = event |
| .getNewStructuredDocumentRegions(); |
| JSONModelUpdater updater = getActiveUpdater(); |
| if (updater != null) { // being updated |
| try { |
| updater.replaceStructuredDocumentRegions( |
| newStructuredDocumentRegions, |
| oldStructuredDocumentRegions); |
| } catch (Exception ex) { |
| Logger.logException(ex); |
| this.refresh = true; |
| handleRefresh(); |
| } finally { |
| setActive(null); |
| } |
| // checkForReinit(); |
| return; |
| } |
| JSONModelNotifier notifier = getModelNotifier(); |
| boolean isChanging = notifier.isChanging(); |
| if (!isChanging) |
| notifier.beginChanging(); |
| JSONModelParser parser = getModelParser(); |
| setActive(parser); |
| try { |
| /* workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=486860 */ |
| // this.refresh = true; |
| // handleRefresh(); |
| boolean reloadModel = false; |
| // Check if the insertion is between two previously existing JSON Nodes, in that case |
| // the model is reloaded completely. |
| if (newStructuredDocumentRegions != null) { |
| int newCount = newStructuredDocumentRegions.getLength(); |
| for (int i = 0; i < newCount -1; i++) { |
| if (newStructuredDocumentRegions.item(i).getType().equals(JSONRegionContexts.JSON_COMMA)) { |
| IStructuredDocumentRegion nextNode = newStructuredDocumentRegions.item(i).getNext(); |
| while (nextNode != null) { |
| if (nextNode.getType().equals(JSONRegionContexts.JSON_OBJECT_KEY) |
| || nextNode.getType().equals(JSONRegionContexts.JSON_OBJECT_OPEN) |
| || nextNode.getType().equals(JSONRegionContexts.JSON_ARRAY_OPEN)) { |
| reloadModel = true; |
| break; |
| } |
| nextNode = nextNode.getNext(); |
| } |
| } |
| } |
| } |
| if (!reloadModel && oldStructuredDocumentRegions != null && oldStructuredDocumentRegions.getLength() > 0) { |
| if (oldStructuredDocumentRegions.getLength() > 3 || oldStructuredDocumentRegions.item(0).getType().equals(JSONRegionContexts.JSON_OBJECT_OPEN)) { |
| // Reload all the model when the first region that will be |
| // replaced is a JSONObject or if more than 3 regions are |
| // replaced in the model (a JSONPair is composed by 3 regions, |
| // this means more that one JSON Pair are replaced in the model) |
| |
| reloadModel = true; |
| } else { |
| // also, always reload the model in case of removing at least one UNDEFINED region |
| for (int i = 0; !reloadModel && i < oldStructuredDocumentRegions.getLength(); i++) { |
| if (oldStructuredDocumentRegions.item(i).getType().equals(JSONRegionContexts.UNDEFINED)) { |
| reloadModel = true; |
| } |
| } |
| } |
| } |
| if(reloadModel) { |
| this.refresh = true; |
| handleRefresh(); |
| } |
| else { |
| parser.replaceStructuredDocumentRegions(newStructuredDocumentRegions, oldStructuredDocumentRegions); |
| } |
| } catch (Exception ex) { |
| if (ex.getClass().equals( |
| StructuredDocumentRegionManagementException.class)) { |
| Logger.traceException(TRACE_PARSER_MANAGEMENT_EXCEPTION, ex); |
| } else { |
| Logger.logException(ex); |
| } |
| this.refresh = true; |
| handleRefresh(); |
| } finally { |
| setActive(null); |
| if (!isChanging) { |
| notifier.endChanging(); |
| handleRefresh(); |
| } |
| } |
| |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.wst.sse.core.internal.provisional.events. |
| * IStructuredDocumentListener |
| * #regionChanged(org.eclipse.wst.sse.core.internal |
| * .provisional.events.RegionChangedEvent) |
| */ |
| @Override |
| public void regionChanged(RegionChangedEvent event) { |
| if (event == null) |
| return; |
| IStructuredDocumentRegion flatNode = event |
| .getStructuredDocumentRegion(); |
| if (flatNode == null) |
| return; |
| ITextRegion region = event.getRegion(); |
| if (region == null) |
| return; |
| JSONModelUpdater updater = getActiveUpdater(); |
| if (updater != null) { // being updated |
| try { |
| updater.changeRegion(event, flatNode, region); |
| } catch (Exception ex) { |
| Logger.logException(ex); |
| this.refresh = true; |
| handleRefresh(); |
| } finally { |
| setActive(null); |
| } |
| // checkForReinit(); |
| return; |
| } |
| JSONModelNotifier notifier = getModelNotifier(); |
| boolean isChanging = notifier.isChanging(); |
| if (!isChanging) |
| notifier.beginChanging(); |
| JSONModelParser parser = getModelParser(); |
| setActive(parser); |
| try { |
| parser.changeRegion(event, flatNode, region); |
| /* workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=486860 */ |
| // this.refresh = true; |
| // handleRefresh(); |
| } catch (Exception ex) { |
| Logger.logException(ex); |
| this.refresh = true; |
| handleRefresh(); |
| } finally { |
| setActive(null); |
| if (!isChanging) { |
| notifier.endChanging(); |
| handleRefresh(); |
| } |
| } |
| // checkForReinit(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.wst.sse.core.internal.provisional.events. |
| * IStructuredDocumentListener |
| * #regionsReplaced(org.eclipse.wst.sse.core.internal |
| * .provisional.events.RegionsReplacedEvent) |
| */ |
| @Override |
| public void regionsReplaced(RegionsReplacedEvent event) { |
| if (event == null) |
| return; |
| IStructuredDocumentRegion flatNode = event |
| .getStructuredDocumentRegion(); |
| if (flatNode == null) |
| return; |
| ITextRegionList oldRegions = event.getOldRegions(); |
| ITextRegionList newRegions = event.getNewRegions(); |
| if (oldRegions == null && newRegions == null) |
| return; |
| JSONModelUpdater updater = getActiveUpdater(); |
| if (updater != null) { // being updated |
| try { |
| updater.replaceRegions(flatNode, newRegions, oldRegions); |
| } catch (Exception ex) { |
| Logger.logException(ex); |
| this.refresh = true; |
| handleRefresh(); |
| } finally { |
| setActive(null); |
| } |
| // checkForReinit(); |
| return; |
| } |
| JSONModelNotifier notifier = getModelNotifier(); |
| boolean isChanging = notifier.isChanging(); |
| if (!isChanging) |
| notifier.beginChanging(); |
| JSONModelParser parser = getModelParser(); |
| setActive(parser); |
| try { |
| /* workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=486860 */ |
| // this.refresh = true; |
| // handleRefresh(); |
| boolean reloadModel = false; |
| // Check if the insertion is between two previously existing JSON Nodes, in that case |
| // the model is reloaded completely. |
| |
| if (flatNode.getType().equals(JSONRegionContexts.JSON_COMMA)) { |
| IStructuredDocumentRegion nextNode = flatNode.getNext(); |
| while (nextNode != null) { |
| if (nextNode.getType().equals(JSONRegionContexts.JSON_OBJECT_KEY) |
| || nextNode.getType().equals(JSONRegionContexts.JSON_OBJECT_OPEN) |
| || nextNode.getType().equals(JSONRegionContexts.JSON_ARRAY_OPEN)) |
| reloadModel = true; |
| nextNode = nextNode.getNext(); |
| } |
| } |
| if(reloadModel) { |
| this.refresh = true; |
| handleRefresh(); |
| } else { |
| parser.replaceRegions(flatNode, newRegions, oldRegions); |
| } |
| } catch (Exception ex) { |
| Logger.logException(ex); |
| this.refresh = true; |
| handleRefresh(); |
| } finally { |
| setActive(null); |
| if (!isChanging) { |
| notifier.endChanging(); |
| handleRefresh(); |
| } |
| } |
| // checkForReinit(); |
| } |
| |
| /** |
| */ |
| public void releaseFromEdit() { |
| if (!isShared()) { |
| // this.document.releaseStyleSheets(); |
| // this.document.releaseDocumentType(); |
| } |
| super.releaseFromEdit(); |
| } |
| |
| /** |
| */ |
| public void releaseFromRead() { |
| if (!isShared()) { |
| // this.document.releaseStyleSheets(); |
| // this.document.releaseDocumentType(); |
| } |
| super.releaseFromRead(); |
| } |
| |
| /** |
| */ |
| private void setActive(Object active) { |
| this.active = active; |
| // side effect |
| // when ever becomes active, besure tagNameCache is cleared |
| // (and not used) |
| // if (active == null) { |
| // document.activateTagNameCache(true); |
| // } else { |
| // document.activateTagNameCache(false); |
| // } |
| } |
| |
| /** |
| */ |
| // public void setGenerator(ISourceGenerator generator) { |
| // this.generator = generator; |
| // } |
| |
| /** |
| */ |
| public void setModelNotifier(JSONModelNotifier notifier) { |
| this.notifier = notifier; |
| } |
| |
| /** |
| */ |
| public void setModelParser(JSONModelParser parser) { |
| this.parser = parser; |
| } |
| |
| /** |
| */ |
| public void setModelUpdater(JSONModelUpdater updater) { |
| this.updater = updater; |
| } |
| |
| /** |
| * setStructuredDocument method |
| * |
| * @param structuredDocument |
| */ |
| public void setStructuredDocument(IStructuredDocument structuredDocument) { |
| IStructuredDocument oldStructuredDocument = super |
| .getStructuredDocument(); |
| if (structuredDocument == oldStructuredDocument) |
| return; // nothing to do |
| if (oldStructuredDocument != null) |
| oldStructuredDocument.removeDocumentChangingListener(this); |
| super.setStructuredDocument(structuredDocument); |
| if (structuredDocument != null) { |
| internalSetNewDocument(structuredDocument); |
| structuredDocument.addDocumentChangingListener(this); |
| } |
| } |
| |
| /** |
| */ |
| protected void startTagChanged(IJSONObject element) { |
| if (element == null) |
| return; |
| if (getActiveParser() == null) { |
| JSONModelUpdater updater = getModelUpdater(); |
| setActive(updater); |
| updater.initialize(); |
| updater.changeStartTag(element); |
| setActive(null); |
| } |
| getModelNotifier().startTagChanged(element); |
| } |
| |
| protected void valueChanged(IJSONNode node) { |
| if (node == null) |
| return; |
| if (getActiveParser() == null) { |
| JSONModelUpdater updater = getModelUpdater(); |
| setActive(updater); |
| updater.initialize(); |
| updater.changeValue(node); |
| setActive(null); |
| } |
| getModelNotifier().valueChanged(node); |
| } |
| |
| } |