##### Coverage Report - com.ctrip.framework.apollo.portal.service.txtresolver.PropertyResolver

Classes in this File | Line Coverage | Branch Coverage | Complexity
PropertyResolver |
95% | 82/86
87% | 61/70
| 3.3333333333333335;3.333

1 |  |
     package com.ctrip.framework.apollo.portal.service.txtresolver;
2 |  |
3 |  |
     import com.ctrip.framework.apollo.core.dto.ItemChangeSets;
4 |  |
     import com.ctrip.framework.apollo.core.dto.ItemDTO;
5 |  |
     import com.ctrip.framework.apollo.core.exception.BadRequestException;
6 |  |
     import com.ctrip.framework.apollo.common.utils.BeanUtils;
7 |  |
8 |  |
     import org.springframework.stereotype.Component;
9 |  |
10 |  |
     import java.util.HashMap;
11 |  |
     import java.util.HashSet;
12 |  |
     import java.util.List;
13 |  |
     import java.util.Map;
14 |  |
     import java.util.Set;
15 |  |
16 |  |
     /**
17 |  |
      * normal property file resolver.
18 |  |
      * update comment and blank item implement by create new item and delete old item.
19 |  |
      * update normal key/value item implement by update.
20 |  |
      */
21 |  |
     @Component
22 | 1 |
     public class PropertyResolver implements ConfigTextResolver {
23 |  |
24 |  |
       private static final String KV_SEPARATOR = "=";
25 |  |
       private static final String ITEM_SEPARATOR = "\n";
26 |  |
27 |  |
       @Override
28 |  |
       public ItemChangeSets resolve(long namespaceId, String configText, List<ItemDTO> baseItems) {
29 |  |
30 | 11 |
         Map<Integer, ItemDTO> oldLineNumMapItem = BeanUtils.mapByKey("lineNum", baseItems);
31 | 11 |
         Map<String, ItemDTO> oldKeyMapItem = BeanUtils.mapByKey("key", baseItems);
32 |  |
33 |  |
         //remove comment and blank item map.
34 | 11 |
         oldKeyMapItem.remove("");
35 |  |
36 | 11 |
         String[] newItems = configText.split(ITEM_SEPARATOR);
37 |  |
38 | 11 |
         if (isHasRepeatKey(newItems)){
39 | 0 |
           throw new BadRequestException("config text has repeat key please check.");
40 |  |
         }
41 |  |
42 | 11 |
         ItemChangeSets changeSets = new ItemChangeSets();
43 | 11 |
         Map<Integer, String> newLineNumMapItem = new HashMap<Integer, String>();//use for delete blank and comment item
44 | 11 |
         int lineCounter = 1;
45 | 45 |
         for (String newItem : newItems) {
46 | 34 |
           newItem = newItem.trim();
47 | 34 |
           newLineNumMapItem.put(lineCounter, newItem);
48 | 34 |
           ItemDTO oldItemByLine = oldLineNumMapItem.get(lineCounter);
49 |  |
50 |  |
           //comment item
51 | 34 |
           if (isCommentItem(newItem)) {
52 |  |
53 | 5 |
             handleCommentLine(namespaceId, oldItemByLine, newItem, lineCounter, changeSets);
54 |  |
55 |  |
             //blank item
56 | 29 |
           } else if (isBlankItem(newItem)) {
57 |  |
58 | 5 |
             handleBlankLine(namespaceId, oldItemByLine, lineCounter, changeSets);
59 |  |
60 |  |
             //normal item
61 |  |
           } else {
62 | 24 |
             handleNormalLine(namespaceId, oldKeyMapItem, newItem, lineCounter, changeSets);
63 |  |
           }
64 |  |
65 | 34 |
           lineCounter++;
66 |  |
         }
67 |  |
68 | 11 |
         deleteCommentAndBlankItem(oldLineNumMapItem, newLineNumMapItem, changeSets);
69 | 11 |
         deleteNormalKVItem(oldKeyMapItem, changeSets);
70 |  |
71 | 11 |
         return changeSets;
72 |  |
       }
73 |  |
74 |  |
       private boolean isHasRepeatKey(String[] newItems){
75 | 11 |
         Set<String> keys = new HashSet<>();
76 | 11 |
         int lineCounter = 1;
77 | 11 |
         int keyCount = 0;
78 | 45 |
         for (String item: newItems){
79 | 34 |
           if (!isCommentItem(item) && !isBlankItem(item)){
80 | 24 |
             keyCount++;
81 | 24 |
             String[] kv = parseKeyValueFromItem(item);
82 | 24 |
             if (kv != null) {
83 | 24 |
               keys.add(kv[0]);
84 |  |
             }else {
85 | 0 |
               throw new BadRequestException("line:" + lineCounter + " key value must separate by '='");
86 |  |
             }
87 |  |
           }
88 | 34 |
           lineCounter ++;
89 |  |
         }
90 |  |
91 | 11 |
         return keyCount > keys.size();
92 |  |
       }
93 |  |
94 |  |
       private String[] parseKeyValueFromItem(String item){
95 | 48 |
         int kvSeparator = item.indexOf(KV_SEPARATOR);
96 | 48 |
         if (kvSeparator == -1) {
97 | 0 |
           return null;
98 |  |
         }
99 |  |
100 | 48 |
         String[] kv = new String[2];
101 | 48 |
         kv[0] = item.substring(0, kvSeparator).trim();
102 | 48 |
         kv[1] = item.substring(kvSeparator + 1, item.length()).trim();
103 | 48 |
         return kv;
104 |  |
       }
105 |  |
106 |  |
       private void handleCommentLine(Long namespaceId, ItemDTO oldItemByLine, String newItem, int lineCounter, ItemChangeSets changeSets) {
107 | 5 |
         String oldComment = oldItemByLine == null ? "" : oldItemByLine.getComment();
108 |  |
         //create comment. implement update comment by delete old comment and create new comment
109 | 5 |
         if (!(isCommentItem(oldItemByLine) && newItem.equals(oldComment))) {
110 | 4 |
           changeSets.addCreateItem(buildCommentItem(0l, namespaceId, newItem, lineCounter));
111 |  |
         }
112 | 5 |
       }
113 |  |
114 |  |
       private void handleBlankLine(Long namespaceId, ItemDTO oldItem, int lineCounter, ItemChangeSets changeSets) {
115 | 5 |
         if (!isBlankItem(oldItem)) {
116 | 4 |
           changeSets.addCreateItem(buildBlankItem(0l, namespaceId, lineCounter));
117 |  |
         }
118 | 5 |
       }
119 |  |
120 |  |
       private void handleNormalLine(Long namespaceId, Map<String, ItemDTO> keyMapOldItem, String newItem,
121 |  |
                                        int lineCounter, ItemChangeSets changeSets) {
122 |  |
123 | 24 |
         String[] kv = parseKeyValueFromItem(newItem);
124 |  |
125 | 24 |
         if (kv == null) {
126 | 0 |
           throw new BadRequestException("line:" + lineCounter + " key value must separate by '='");
127 |  |
         }
128 |  |
129 | 24 |
         String newKey = kv[0];
130 | 24 |
         String newValue = kv[1].replace("\\n", "\n"); //handle user input \n
131 |  |
132 | 24 |
         ItemDTO oldItem = keyMapOldItem.get(newKey);
133 |  |
134 | 24 |
         if (oldItem == null) {//new item
135 | 5 |
           changeSets.addCreateItem(buildNormalItem(0l, namespaceId, newKey, newValue, "", lineCounter));
136 | 19 |
         } else if (!newValue.equals(oldItem.getValue()) || lineCounter != oldItem.getLineNum()){//update item
137 | 30 |
           changeSets.addUpdateItem(
138 | 15 |
               buildNormalItem(oldItem.getId(), namespaceId, newKey, newValue, oldItem.getComment(),
139 |  |
                               lineCounter));
140 |  |
         }
141 | 24 |
         keyMapOldItem.remove(newKey);
142 | 24 |
       }
143 |  |
144 |  |
       private boolean isCommentItem(ItemDTO item) {
145 | 33 |
         return item != null && "".equals(item.getKey())
146 | 8 |
                && (item.getComment().startsWith("#") || item.getComment().startsWith("!"));
147 |  |
       }
148 |  |
149 |  |
       private boolean isCommentItem(String line) {
150 | 68 |
         return line != null && (line.startsWith("#") || line.startsWith("!"));
151 |  |
       }
152 |  |
153 |  |
       private boolean isBlankItem(ItemDTO item) {
154 | 36 |
         return item != null && "".equals(item.getKey()) && "".equals(item.getComment());
155 |  |
       }
156 |  |
157 |  |
       private boolean isBlankItem(String line) {
158 | 62 |
         return "".equals(line);
159 |  |
       }
160 |  |
161 |  |
       private void deleteNormalKVItem(Map<String, ItemDTO> baseKeyMapItem, ItemChangeSets changeSets) {
162 |  |
         //surplus item is to be deleted
163 | 11 |
         for (Map.Entry<String, ItemDTO> entry : baseKeyMapItem.entrySet()) {
164 | 4 |
           changeSets.addDeleteItem(entry.getValue());
165 | 4 |
         }
166 | 11 |
       }
167 |  |
168 |  |
       private void deleteCommentAndBlankItem(Map<Integer, ItemDTO> oldLineNumMapItem,
169 |  |
                                              Map<Integer, String> newLineNumMapItem,
170 |  |
                                              ItemChangeSets changeSets) {
171 |  |
172 | 11 |
         for (Map.Entry<Integer, ItemDTO> entry : oldLineNumMapItem.entrySet()) {
173 | 31 |
           int lineNum = entry.getKey();
174 | 31 |
           ItemDTO oldItem = entry.getValue();
175 | 31 |
           String newItem = newLineNumMapItem.get(lineNum);
176 |  |
177 |  |
           //1. old is blank by now is not
178 |  |
           //2.old is comment by now is not exist or modified
179 | 31 |
           if ((isBlankItem(oldItem) && !isBlankItem(newItem))
180 | 28 |
               || isCommentItem(oldItem) && (newItem == null || !newItem.equals(oldItem.getComment()))) {
181 | 6 |
             changeSets.addDeleteItem(oldItem);
182 |  |
           }
183 | 31 |
         }
184 | 11 |
       }
185 |  |
186 |  |
       private ItemDTO buildCommentItem(Long id, Long namespaceId, String comment, int lineNum) {
187 | 4 |
         return buildNormalItem(id, namespaceId, "", "", comment, lineNum);
188 |  |
       }
189 |  |
190 |  |
       private ItemDTO buildBlankItem(Long id, Long namespaceId, int lineNum) {
191 | 4 |
         return buildNormalItem(id, namespaceId, "", "", "", lineNum);
192 |  |
       }
193 |  |
194 |  |
       private ItemDTO buildNormalItem(Long id, Long namespaceId, String key, String value, String comment, int lineNum) {
195 | 28 |
         ItemDTO item = new ItemDTO(key, value, comment, lineNum);
196 | 28 |
         item.setId(id);
197 | 28 |
         item.setNamespaceId(namespaceId);
198 | 28 |
         return item;
199 |  |
       }
200 |  |
     }